aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json77
-rw-r--r--.github/workflows/ci_eslint.yml10
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt4
-rw-r--r--ChangeLog10
-rw-r--r--cmake/CompilerWarnings.cmake6
-rw-r--r--cmake/ProcessPackage.cmake7
-rw-r--r--conf/groups.conf7
-rw-r--r--conf/modules.d/gpt.conf6
-rw-r--r--conf/modules.d/once_received.conf3
-rw-r--r--conf/modules.d/rbl.conf48
-rw-r--r--conf/scores.d/headers_group.conf8
-rw-r--r--conf/scores.d/hfilter_group.conf8
-rw-r--r--conf/scores.d/policies_group.conf5
-rw-r--r--conf/scores.d/rbl_group.conf155
-rw-r--r--conf/scores.d/surbl_group.conf4
-rw-r--r--conf/statistic.conf1
-rw-r--r--contrib/DEPENDENCY_INFO.md2
-rw-r--r--contrib/aho-corasick/acism.c2
-rw-r--r--contrib/doctest/CMakeLists.txt109
-rw-r--r--contrib/doctest/doctest/doctest.h3072
-rw-r--r--contrib/doctest/doctest/parts/doctest.cpp1180
-rw-r--r--contrib/doctest/doctest/parts/doctest_fwd.h1886
-rw-r--r--contrib/doctest/scripts/cmake/Config.cmake.in6
-rw-r--r--contrib/doctest/scripts/cmake/assemble_single_header.cmake13
-rw-r--r--contrib/doctest/scripts/cmake/common.cmake212
-rw-r--r--contrib/doctest/scripts/cmake/doctest.cmake189
-rw-r--r--contrib/doctest/scripts/cmake/doctestAddTests.cmake120
-rw-r--r--contrib/doctest/scripts/cmake/exec_test.cmake70
-rw-r--r--contrib/doctest/scripts/version.txt1
-rw-r--r--contrib/kann/kann.c325
-rw-r--r--contrib/librdns/curve.c569
-rw-r--r--contrib/libucl/lua_ucl.c489
-rw-r--r--contrib/libucl/lua_ucl.h9
-rw-r--r--contrib/publicsuffix/README.md13
-rw-r--r--contrib/publicsuffix/cpanfile3
-rw-r--r--contrib/publicsuffix/effective_tld_names.dat2023
-rwxr-xr-x[-rw-r--r--]contrib/publicsuffix/idn.pl0
-rw-r--r--contrib/zstd/CMakeLists.txt2
-rw-r--r--eslint.config.mjs84
-rw-r--r--interface/js/app/libft.js6
-rw-r--r--interface/js/app/stats.js1
-rw-r--r--interface/js/app/symbols.js4
-rw-r--r--lualib/lua_bayes_learn.lua13
-rw-r--r--lualib/lua_cfg_transform.lua603
-rw-r--r--lualib/lua_maps.lua16
-rw-r--r--lualib/lua_redis.lua214
-rw-r--r--lualib/lua_scanners/icap.lua2
-rw-r--r--lualib/lua_util.lua52
-rw-r--r--package-lock.json2554
-rw-r--r--package.json13
-rw-r--r--rules/archives.lua38
-rw-r--r--rules/misc.lua68
-rw-r--r--src/client/rspamc.cxx17
-rw-r--r--src/client/rspamdclient.c6
-rw-r--r--src/controller.c10
-rw-r--r--src/fuzzy_storage.c13
-rw-r--r--src/libcryptobox/cryptobox.c999
-rw-r--r--src/libcryptobox/cryptobox.h405
-rw-r--r--src/libcryptobox/keypair.c276
-rw-r--r--src/libcryptobox/keypair.h31
-rw-r--r--src/libcryptobox/keypair_private.h46
-rw-r--r--src/libcryptobox/keypairs_cache.c27
-rw-r--r--src/libmime/lang_detection.c2
-rw-r--r--src/libmime/scan_result.c48
-rw-r--r--src/libserver/cfg_file.h1
-rw-r--r--src/libserver/cfg_rcl.cxx44
-rw-r--r--src/libserver/cfg_rcl.h3
-rw-r--r--src/libserver/cfg_utils.cxx2
-rw-r--r--src/libserver/dkim.c263
-rw-r--r--src/libserver/http/http_connection.c331
-rw-r--r--src/libserver/http/http_context.c3
-rw-r--r--src/libserver/logger/logger.c20
-rw-r--r--src/libserver/maps/map.c13
-rw-r--r--src/libserver/milter.c8
-rw-r--r--src/libserver/protocol.c19
-rw-r--r--src/libserver/protocol_internal.h1
-rw-r--r--src/libserver/spf.c5
-rw-r--r--src/libserver/spf.h2
-rw-r--r--src/libserver/symcache/symcache_runtime.cxx43
-rw-r--r--src/libserver/symcache/symcache_runtime.hxx7
-rw-r--r--src/libstat/stat_process.c20
-rw-r--r--src/lua/lua_config.c854
-rw-r--r--src/lua/lua_cryptobox.c154
-rw-r--r--src/lua/lua_http.c5
-rw-r--r--src/lua/lua_map.c3
-rw-r--r--src/lua/lua_redis.c23
-rw-r--r--src/lua/lua_rsa.c164
-rw-r--r--src/lua/lua_spf.c7
-rw-r--r--src/lua/lua_task.c1
-rw-r--r--src/plugins/fuzzy_check.c14
-rw-r--r--src/plugins/lua/arc.lua12
-rw-r--r--src/plugins/lua/gpt.lua41
-rw-r--r--src/plugins/lua/history_redis.lua4
-rw-r--r--src/plugins/lua/known_senders.lua3
-rw-r--r--src/plugins/lua/once_received.lua103
-rw-r--r--src/plugins/lua/spf.lua3
-rw-r--r--src/rspamadm/configdump.c5
-rw-r--r--src/rspamadm/signtool.c37
-rw-r--r--src/rspamd_proxy.c14
-rw-r--r--test/functional/cases/001_merged/101_lua.robot46
-rw-r--r--test/functional/cases/001_merged/117_spf.robot6
-rw-r--r--test/functional/cases/450_grow_factor.robot17
-rw-r--r--test/functional/cases/550_milter_headers.robot39
-rw-r--r--test/functional/configs/grow_factor-local.conf4
-rw-r--r--test/functional/configs/merged-local.conf44
-rw-r--r--test/functional/configs/merged.conf1
-rw-r--r--test/functional/configs/milter_headers.conf24
-rw-r--r--test/functional/lib/grow_factor.py32
-rw-r--r--test/functional/lib/rspamd.robot60
-rw-r--r--test/functional/lua/deps.lua2
-rw-r--r--test/functional/lua/limits.lua22
-rw-r--r--test/functional/lua/simple_plus.lua4
-rw-r--r--test/lua/unit/rsa.lua10
-rw-r--r--test/lua/unit/ucl.lua75
-rw-r--r--test/rspamd_cryptobox_test.c57
-rw-r--r--test/rspamd_cxx_unit.cxx3
-rw-r--r--test/rspamd_cxx_unit_cryptobox.hxx182
118 files changed, 12065 insertions, 6998 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index a6f4e1129..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- "env": {
- "browser": true,
- "es6": true
- },
- "extends": [
- "eslint:all",
- "plugin:@stylistic/all-extends"
- ],
- "globals": {
- "define": false
- },
- "parserOptions": {
- "ecmaVersion": 2016
- },
- "plugins": [
- "@stylistic"
- ],
- "rules": {
- "camelcase": "off",
- "capitalized-comments": "off",
- "curly": ["error", "multi-line"],
- "func-names": "off",
- "func-style": ["error", "declaration"],
- "id-length": ["error", { "min": 1 }],
- "line-comment-position": "off",
- "logical-assignment-operators": ["error", "never"],
- "max-params": ["warn", 6],
- "max-statements": ["warn", 55],
- "multiline-comment-style": "off",
- "no-continue": "off",
- "no-inline-comments": "off",
- "no-magic-numbers": "off",
- "no-negated-condition": "off",
- "no-plusplus": "off",
- "no-ternary": "off",
- "object-shorthand": "off",
- "one-var": ["error", { "initialized": "never" }],
- "prefer-named-capture-group": "off",
- "prefer-object-has-own": "off",
- "prefer-spread": "off",
- "prefer-template": "off",
- "require-unicode-regexp": "off",
-
- "@stylistic/array-bracket-newline": ["error", "consistent"],
- "@stylistic/array-element-newline": "off",
- "@stylistic/brace-style": ["error", "1tbs", { "allowSingleLine": true }],
- "@stylistic/comma-dangle": ["error", "only-multiline"],
- "@stylistic/dot-location": ["error", "property"],
- "@stylistic/function-call-argument-newline": "off",
- "@stylistic/max-len": ["error", { "code": 128 }],
- "@stylistic/max-statements-per-line": ["error", { "max": 2 }],
- "@stylistic/multiline-comment-style": "off",
- "@stylistic/multiline-ternary": ["error", "always-multiline"],
- "@stylistic/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 5 }],
- "@stylistic/no-extra-parens": ["error", "functions"],
- "@stylistic/object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }],
- "@stylistic/padded-blocks": ["error", "never"],
- "@stylistic/quote-props" : ["error", "consistent-as-needed"],
- "@stylistic/quotes": ["error", "double", { "avoidEscape": true }],
- "@stylistic/semi": ["error", "always"],
- "@stylistic/space-before-function-paren": ["error", {
- "anonymous": "always",
- "named": "never"
- }],
-
-
- // Temporarily disabled rules
- "max-lines": "off",
- "max-lines-per-function": "off",
- "no-invalid-this": "off",
- "sort-keys": "off",
-
- "@stylistic/function-paren-newline": "off",
- "@stylistic/indent-binary-ops": "off"
- }
-}
diff --git a/.github/workflows/ci_eslint.yml b/.github/workflows/ci_eslint.yml
index e7220d468..a4fe0fc31 100644
--- a/.github/workflows/ci_eslint.yml
+++ b/.github/workflows/ci_eslint.yml
@@ -6,14 +6,16 @@ on:
jobs:
eslint:
runs-on: ubuntu-latest
- container:
- image: node:18-alpine
steps:
- name: Check out source code
uses: actions/checkout@v4
- - name: Install dependencies
- run: npm install
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install packages
+ run: npm ci
- name: Show installed packages
run: npm ls
diff --git a/.gitignore b/.gitignore
index 613e9eaff..2ea0abc60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+node_modules/
# Code::TidyAll
/.tidyall.d/
.idea
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e76939da2..a9bb50642 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12 FATAL_ERROR)
SET(RSPAMD_VERSION_MAJOR 3)
SET(RSPAMD_VERSION_MINOR 9)
-SET(RSPAMD_VERSION_PATCH 1)
+SET(RSPAMD_VERSION_PATCH 2)
# Keep two digits all the time
SET(RSPAMD_VERSION_MAJOR_NUM ${RSPAMD_VERSION_MAJOR}0)
@@ -267,6 +267,8 @@ ADD_DEFINITIONS(-DFMT_HEADER_ONLY)
# Workaround for https://github.com/onqtam/doctest/issues/356
ADD_DEFINITIONS(-DDOCTEST_CONFIG_USE_STD_HEADERS)
ADD_DEFINITIONS(-DU_CHARSET_IS_UTF8)
+# Disable zstd deprecation warnings, as they are not relevant for us
+ADD_DEFINITIONS(-DZSTD_DISABLE_DEPRECATE_WARNINGS)
# Check platform specific includes
CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H)
diff --git a/ChangeLog b/ChangeLog
index 085581353..f749c7887 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+3.9.1: 23 Jul 2024
+ * [Conf] Spf: Add R_SPF_PLUSALL symbol with some score
+ * [Feature] Spf: Treat SPF +all in a special way
+ * [Minor] Ensure some safety when checking weights
+ * [Minor] Fix several issues with flag propagation
+ * [Minor] Gpt: Improve prompt and add some conversion heursitics
+ * [Minor] Gpt: Remove top_p reduce temperature to 0
+ * [Minor] Gpt: Set response_format
+ * [Minor] Gpt: Use gpt-4o-mini by default
+
3.9.0: 12 Jul 2024
* [CritFix] Protect regexp matcher from regexps with empty patterns
* [Feature] Allow adding X-CMAE-Score header
diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake
index 9092457de..2315d5e4d 100644
--- a/cmake/CompilerWarnings.cmake
+++ b/cmake/CompilerWarnings.cmake
@@ -84,6 +84,6 @@ IF(SUPPORT_WSUGGEST_ATTRIBUTE)
ADD_COMPILE_OPTIONS("-Wno-suggest-attribute=format")
ENDIF()
-IF(SUPPORT_WDEPRECATED_DECLARATIONS)
- ADD_COMPILE_OPTIONS("-Wno-deprecated-declarations")
-ENDIF()
+#IF(SUPPORT_WDEPRECATED_DECLARATIONS)
+# ADD_COMPILE_OPTIONS("-Wno-deprecated-declarations")
+#ENDIF()
diff --git a/cmake/ProcessPackage.cmake b/cmake/ProcessPackage.cmake
index a46fd85b4..316662568 100644
--- a/cmake/ProcessPackage.cmake
+++ b/cmake/ProcessPackage.cmake
@@ -32,7 +32,12 @@ MACRO(ProcessPackage PKG_NAME)
ENDIF()
IF(${PKG_NAME}_FOUND)
- MESSAGE(STATUS "Found package ${PKG_NAME} in pkg-config modules ${PKG_MODULES}")
+ SET(_ver ${${PKG_NAME}_VERSION})
+ IF(_ver)
+ MESSAGE(STATUS "Found package ${PKG_NAME} (${_ver}) in pkg-config modules ${PKG_MODULES}")
+ ELSE()
+ MESSAGE(STATUS "Found package ${PKG_NAME} in pkg-config modules ${PKG_MODULES}")
+ ENDIF()
SET(WITH_${PKG_NAME} 1 CACHE INTERNAL "")
IF(ENABLE_STATIC MATCHES "ON")
SET(_XPREFIX "${PKG_NAME}_STATIC")
diff --git a/conf/groups.conf b/conf/groups.conf
index 2aeb4ed5d..4f40d865c 100644
--- a/conf/groups.conf
+++ b/conf/groups.conf
@@ -39,6 +39,13 @@ group "rbl" {
.include(try=true; priority=10) "$LOCAL_CONFDIR/override.d/rbl_group.conf"
}
+# Limits the maximum score when both bl.score.senderscore.com and score.senderscore.com RBLs are enabled.
+group "senderscore" {
+ max_score = 4.0;
+ .include(try=true; priority=1; duplicate=merge) "$LOCAL_CONFDIR/local.d/senderscore_group.conf"
+ .include(try=true; priority=10) "$LOCAL_CONFDIR/override.d/senderscore_group.conf"
+}
+
group "statistics" {
.include "$CONFDIR/scores.d/statistics_group.conf"
.include(try=true; priority=1; duplicate=merge) "$LOCAL_CONFDIR/local.d/statistics_group.conf"
diff --git a/conf/modules.d/gpt.conf b/conf/modules.d/gpt.conf
index 7a2e11d40..c76a08c92 100644
--- a/conf/modules.d/gpt.conf
+++ b/conf/modules.d/gpt.conf
@@ -18,13 +18,11 @@ gpt {
# Your key to access the API (add this to enable this plugin)
#api_key = "xxx";
# Model name
- model = "gpt-3.5-turbo";
+ model = "gpt-4o-mini";
# Maximum tokens to generate
max_tokens = 1000;
# Temperature for sampling
- temperature = 0.7;
- # Top p for sampling
- top_p = 0.9;
+ temperature = 0.0;
# Timeout for requests
timeout = 10s;
# Prompt for the model (use default if not set)
diff --git a/conf/modules.d/once_received.conf b/conf/modules.d/once_received.conf
index ab0749295..6fcc35bb6 100644
--- a/conf/modules.d/once_received.conf
+++ b/conf/modules.d/once_received.conf
@@ -14,8 +14,7 @@
once_received {
good_host = "mail";
- bad_host = "static";
- bad_host = "dynamic";
+ bad_host = ["static", "dynamic"];
symbol_strict = "ONCE_RECEIVED_STRICT";
symbol = "ONCE_RECEIVED";
symbol_mx = "DIRECT_TO_MX";
diff --git a/conf/modules.d/rbl.conf b/conf/modules.d/rbl.conf
index c3594dbc9..2a718e5a4 100644
--- a/conf/modules.d/rbl.conf
+++ b/conf/modules.d/rbl.conf
@@ -79,9 +79,55 @@ rbl {
}
senderscore {
- symbol = "RBL_SENDERSCORE";
+ # Disabled by default to prioritize the use of score.senderscore.com.
+ # Note: The free query limit applies to both bl.score.senderscore.com and score.senderscore.com RBLs
+ # (see https://knowledge.validity.com/hc/en-us/articles/20961730681243).
+ # Enabling this RBL is recommended for low-traffic systems or MyValidity account users who benefit from using both RBLs.
+ enabled = false;
+ symbol = "RBL_SENDERSCORE_UNKNOWN";
checks = ['from'];
rbl = "bl.score.senderscore.com";
+ returncodes {
+ RBL_SENDERSCORE_BOT = "127.0.0.1";
+ RBL_SENDERSCORE_NA = "127.0.0.2";
+ RBL_SENDERSCORE_NA_BOT = "127.0.0.3";
+ RBL_SENDERSCORE_PRST = "127.0.0.4";
+ RBL_SENDERSCORE_PRST_BOT = "127.0.0.5";
+ RBL_SENDERSCORE_PRST_NA = "127.0.0.6";
+ RBL_SENDERSCORE_PRST_NA_BOT = "127.0.0.7";
+ RBL_SENDERSCORE_SUS_ATT = "127.0.0.8";
+ RBL_SENDERSCORE_SUS_ATT_NA = "127.0.0.10";
+ RBL_SENDERSCORE_SUS_ATT_NA_BOT = "127.0.0.11";
+ RBL_SENDERSCORE_SUS_ATT_PRST_NA = "127.0.0.14";
+ RBL_SENDERSCORE_SUS_ATT_PRST_NA_BOT = "127.0.0.15";
+ RBL_SENDERSCORE_SCORE = "127.0.0.16";
+ RBL_SENDERSCORE_SCORE_NA = "127.0.0.18";
+ RBL_SENDERSCORE_SCORE_PRST = "127.0.0.20";
+ RBL_SENDERSCORE_SCORE_PRST_NA = "127.0.0.22";
+ RBL_SENDERSCORE_SCORE_SUS_ATT_NA = "127.0.0.26";
+ RBL_SENDERSCORE_BLOCKED = "127.255.255.255";
+ }
+ }
+
+ senderscore_reputation {
+ symbol = "RBL_SENDERSCORE_REPUT_UNKNOWN";
+ checks = ['from'];
+ rbl = "score.senderscore.com";
+ returncodes_matcher = "luapattern";
+
+ returncodes {
+ RBL_SENDERSCORE_REPUT_0 = "127%.0%.4%.%d";
+ RBL_SENDERSCORE_REPUT_1 = "127%.0%.4%.1%d";
+ RBL_SENDERSCORE_REPUT_2 = "127%.0%.4%.2%d";
+ RBL_SENDERSCORE_REPUT_3 = "127%.0%.4%.3%d";
+ RBL_SENDERSCORE_REPUT_4 = "127%.0%.4%.4%d";
+ RBL_SENDERSCORE_REPUT_5 = "127%.0%.4%.5%d";
+ RBL_SENDERSCORE_REPUT_6 = "127%.0%.4%.6%d";
+ RBL_SENDERSCORE_REPUT_7 = "127%.0%.4%.7%d";
+ RBL_SENDERSCORE_REPUT_8 = "127%.0%.4%.8%d"; # Neutral reputation (80-89).
+ RBL_SENDERSCORE_REPUT_9 = ["127%.0%.4%.9%d", "127%.0%.4%.100"]; # Good reputation (90-100).
+ RBL_SENDERSCORE_REPUT_BLOCKED = "127%.255%.255%.255";
+ }
}
sem {
diff --git a/conf/scores.d/headers_group.conf b/conf/scores.d/headers_group.conf
index 1c70ca588..972c6872a 100644
--- a/conf/scores.d/headers_group.conf
+++ b/conf/scores.d/headers_group.conf
@@ -50,14 +50,6 @@ symbols = {
weight = 0.1;
description = "One received header in a message";
}
- "RDNS_NONE" {
- weight = 2.0;
- description = "Cannot resolve reverse DNS for sender's IP";
- }
- "RDNS_DNSFAIL" {
- weight = 0.0;
- description = "PTR verification DNS error";
- }
"ONCE_RECEIVED_STRICT" {
weight = 4.0;
description = "One received header with 'bad' patterns inside";
diff --git a/conf/scores.d/hfilter_group.conf b/conf/scores.d/hfilter_group.conf
index 09fcfcd8d..21cd11a60 100644
--- a/conf/scores.d/hfilter_group.conf
+++ b/conf/scores.d/hfilter_group.conf
@@ -130,4 +130,12 @@ symbols = {
weight = 2.5;
description = "One line URL and text in body";
}
+ "RDNS_NONE" {
+ weight = 2.0;
+ description = "Cannot resolve reverse DNS for sender's IP";
+ }
+ "RDNS_DNSFAIL" {
+ weight = 0.0;
+ description = "PTR verification DNS error";
+ }
}
diff --git a/conf/scores.d/policies_group.conf b/conf/scores.d/policies_group.conf
index 4a8bdb6b7..712c61523 100644
--- a/conf/scores.d/policies_group.conf
+++ b/conf/scores.d/policies_group.conf
@@ -55,6 +55,11 @@ symbols = {
description = "SPF record is malformed or persistent DNS error";
groups = ["spf"];
}
+ "R_SPF_PLUSALL" {
+ weight = 4.0;
+ description = "SPF record allows to send from any IP";
+ groups = ["spf"];
+ }
# DKIM
"R_DKIM_REJECT" {
diff --git a/conf/scores.d/rbl_group.conf b/conf/scores.d/rbl_group.conf
index ef29ed2fa..6a59b865f 100644
--- a/conf/scores.d/rbl_group.conf
+++ b/conf/scores.d/rbl_group.conf
@@ -162,9 +162,160 @@ symbols = {
groups = ["spamhaus", "blocked"];
}
- "RBL_SENDERSCORE" {
+ "RBL_SENDERSCORE_UNKNOWN" {
+ weight = 0.0;
+ description = "Unrecognised result from SenderScore RPBL";
+ }
+ "RBL_SENDERSCORE_BOT" {
+ weight = 2.0;
+ description = "From address is listed in SenderScore RPBL - botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_NA" {
+ weight = 0.0;
+ description = "From address is listed in SenderScore RPBL - noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_NA_BOT" {
+ weight = 1.0;
+ description = "From address is listed in SenderScore RPBL - noauth+botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_PRST" {
weight = 2.0;
- description = "From address is listed in senderscore.com BL";
+ description = "From address is listed in SenderScore RPBL - pristine";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_PRST_BOT" {
+ weight = 3.0;
+ description = "From address is listed in SenderScore RPBL - pristine+botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_PRST_NA" {
+ weight = 2.0;
+ description = "From address is listed in SenderScore RPBL - pristine+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_PRST_NA_BOT" {
+ weight = 3.0;
+ description = "From address is listed in SenderScore RPBL - pristine+noauth+botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SUS_ATT" {
+ weight = 1.0;
+ description = "From address is listed in SenderScore RPBL - suspect_attachments";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SUS_ATT_NA" {
+ weight = 1.0;
+ description = "From address is listed in SenderScore RPBL - suspect_attachments+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SUS_ATT_NA_BOT" {
+ weight = 1.5;
+ description = "From address is listed in SenderScore RPBL - suspect_attachments+noauth+botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SUS_ATT_PRST_NA" {
+ weight = 3.0;
+ description = "From address is listed in SenderScore RPBL - suspect_attachments+pristine+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SUS_ATT_PRST_NA_BOT" {
+ weight = 3.5;
+ description = "From address is listed in SenderScore RPBL - suspect_attachments+pristine+noauth+botnet";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SCORE" {
+ weight = 2.0;
+ description = "From address is listed in SenderScore RPBL - sender_score";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SCORE_NA" {
+ weight = 2.0;
+ description = "From address is listed in SenderScore RPBL - sender_score+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SCORE_PRST" {
+ weight = 4.0;
+ description = "From address is listed in SenderScore RPBL - sender_score+pristine";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SCORE_PRST_NA" {
+ weight = 4.0;
+ description = "From address is listed in SenderScore RPBL - sender_score+pristine+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_SCORE_SUS_ATT_NA" {
+ weight = 3.0;
+ description = "From address is listed in SenderScore RPBL - sender_score+suspect_attachments+noauth";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_BLOCKED" {
+ weight = 0.0;
+ description = "Excessive number of queries to SenderScore RPBL, more info: https://knowledge.validity.com/hc/en-us/articles/20961730681243";
+ groups = ["senderscore", "blocked"];
+ }
+
+ "RBL_SENDERSCORE_REPUT_UNKNOWN" {
+ weight = 0.0;
+ description = "Unrecognized result from SenderScore Reputation list.";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_0" {
+ weight = 4.0;
+ description = "SenderScore Reputation: Very Bad (0-9).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_1" {
+ weight = 3.5;
+ description = "SenderScore Reputation: Bad (10-19).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_2" {
+ weight = 3.0;
+ description = "SenderScore Reputation: Bad (20-29).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_3" {
+ weight = 2.5;
+ description = "SenderScore Reputation: Bad (30-39).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_4" {
+ weight = 2.0;
+ description = "SenderScore Reputation: Bad (40-49).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_5" {
+ weight = 1.5;
+ description = "SenderScore Reputation: Bad (50-59).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_6" {
+ weight = 1.0;
+ description = "SenderScore Reputation: Bad (60-69).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_7" {
+ weight = 0.5;
+ description = "SenderScore Reputation: Bad (70-79).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_8" {
+ weight = 0.0;
+ description = "SenderScore Reputation: Neutral (80-89).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_9" {
+ weight = -1.0;
+ description = "SenderScore Reputation: Good (90-100).";
+ groups = ["senderscore"];
+ }
+ "RBL_SENDERSCORE_REPUT_BLOCKED" {
+ weight = 0.0;
+ description = "Excessive number of queries to SenderScore RPBL, more info: https://knowledge.validity.com/hc/en-us/articles/20961730681243";
+ groups = ["senderscore", "blocked"];
}
"MAILSPIKE" {
diff --git a/conf/scores.d/surbl_group.conf b/conf/scores.d/surbl_group.conf
index 89579ca15..255c03214 100644
--- a/conf/scores.d/surbl_group.conf
+++ b/conf/scores.d/surbl_group.conf
@@ -214,13 +214,13 @@ symbols = {
groups = ["uribl"];
}
"URIBL_RED" {
- weight = 3.5;
+ weight = 0.5;
description = "A domain in the message is listed in URIBL.com red";
one_shot = true;
groups = ["uribl"];
}
"URIBL_GREY" {
- weight = 1.5;
+ weight = 2.5;
description = "A domain in the message is listed in URIBL.com grey";
one_shot = true;
groups = ["uribl"];
diff --git a/conf/statistic.conf b/conf/statistic.conf
index 0ba8302e8..36d418935 100644
--- a/conf/statistic.conf
+++ b/conf/statistic.conf
@@ -19,6 +19,7 @@
# Module documentation: https://rspamd.com/doc/configuration/statistic.html
classifier "bayes" {
+ # name = "custom"; # 'name' parameter must be set if multiple classifiers are defined
tokenizer {
name = "osb";
}
diff --git a/contrib/DEPENDENCY_INFO.md b/contrib/DEPENDENCY_INFO.md
index 2b57f9796..ccdde60bb 100644
--- a/contrib/DEPENDENCY_INFO.md
+++ b/contrib/DEPENDENCY_INFO.md
@@ -33,7 +33,7 @@
| expected | v1.0 | Public Domain / CC0 | NO | |
| frozen | 1.0.1 | Apache 2 | NO | |
| fmt | 11.0.0 | MIT | NO | |
-| doctest | 2.4.6 | MIT | NO | |
+| doctest | 2.4.11 | MIT | NO | |
| function2 | 4.1.0 | Boost | NO | |
| ankerl/svector | 1.0.2 | MIT | NO | |
| ankerl/unordered_dense | 4.4.0 | MIT | NO | |
diff --git a/contrib/aho-corasick/acism.c b/contrib/aho-corasick/acism.c
index b0cee0d66..7451a8791 100644
--- a/contrib/aho-corasick/acism.c
+++ b/contrib/aho-corasick/acism.c
@@ -38,8 +38,6 @@
#include "_acism.h"
#include "unix-std.h"
-#define BACK ((SYMBOL) 0)
-#define ROOT ((STATE) 0)
extern const unsigned char lc_map[256];
int acism_lookup(ac_trie_t const *psp, const char *text, size_t len,
diff --git a/contrib/doctest/CMakeLists.txt b/contrib/doctest/CMakeLists.txt
index 4a17708ff..c6b3f48ee 100644
--- a/contrib/doctest/CMakeLists.txt
+++ b/contrib/doctest/CMakeLists.txt
@@ -8,7 +8,8 @@ endif()
## DOCTEST
################################################################################
-project(doctest VERSION 2.4.6 LANGUAGES CXX)
+file(READ ${CMAKE_CURRENT_SOURCE_DIR}/scripts/version.txt ver)
+project(doctest VERSION ${ver} LANGUAGES CXX)
# Determine if doctest is built as a subproject (using add_subdirectory) or if it is the main project.
set(MAIN_PROJECT OFF)
@@ -18,7 +19,7 @@ endif()
option(DOCTEST_WITH_TESTS "Build tests/examples" ${MAIN_PROJECT})
option(DOCTEST_WITH_MAIN_IN_STATIC_LIB "Build a static lib (cmake target) with a default main entry point" ON)
-option(DOCTEST_NO_INSTALL "Skip the installation process" OFF)
+option(DOCTEST_NO_INSTALL "Skip the installation process" ON)
option(DOCTEST_USE_STD_HEADERS "Use std headers" OFF)
add_library(${PROJECT_NAME} INTERFACE)
@@ -52,6 +53,14 @@ else()
target_include_directories(${PROJECT_NAME} INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>)
endif()
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ if (NOT WIN32)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4 ")
+ endif()
+ endif()
+endif()
+
# hack to support building on XCode 6 and 7 - propagate the definition to everything
if(DEFINED DOCTEST_THREAD_LOCAL)
target_compile_definitions(${PROJECT_NAME} INTERFACE
@@ -61,3 +70,99 @@ endif()
if(DOCTEST_USE_STD_HEADERS)
target_compile_definitions(${PROJECT_NAME} INTERFACE DOCTEST_CONFIG_USE_STD_HEADERS)
endif()
+
+################################################################################
+## TESTS/EXAMPLES/HELPERS
+################################################################################
+
+if(${DOCTEST_WITH_MAIN_IN_STATIC_LIB})
+ add_library(${PROJECT_NAME}_with_main STATIC EXCLUDE_FROM_ALL ${doctest_parts_folder}/doctest.cpp)
+ add_library(${PROJECT_NAME}::${PROJECT_NAME}_with_main ALIAS ${PROJECT_NAME}_with_main)
+ target_compile_definitions(${PROJECT_NAME}_with_main PRIVATE
+ DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN)
+ set_target_properties(${PROJECT_NAME}_with_main PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
+ target_link_libraries(${PROJECT_NAME}_with_main PUBLIC ${PROJECT_NAME})
+endif()
+
+if(MAIN_PROJECT AND DOCTEST_WITH_TESTS)
+ include(scripts/cmake/common.cmake)
+
+ add_subdirectory(examples/all_features)
+ add_subdirectory(examples/exe_with_static_libs)
+ add_subdirectory(examples/executable_dll_and_plugin)
+ add_subdirectory(examples/combining_the_same_tests_built_differently_in_multiple_shared_objects)
+ add_subdirectory(scripts/playground)
+ add_subdirectory(examples/mpi)
+endif()
+
+################################################################################
+## PACKAGE SUPPORT
+################################################################################
+
+set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
+
+if(CMAKE_SYSTEM_NAME STREQUAL Linux)
+ include(GNUInstallDirs)
+ set(include_install_dir ${CMAKE_INSTALL_INCLUDEDIR})
+ set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+else()
+ set(include_install_dir "include")
+ set(config_install_dir "lib/cmake/${PROJECT_NAME}")
+endif()
+
+
+set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
+set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
+set(targets_export_name "${PROJECT_NAME}Targets")
+set(namespace "${PROJECT_NAME}::")
+
+include(CMakePackageConfigHelpers)
+
+# CMake automatically adds an architecture compatibility check to make sure
+# 32 and 64 bit code is not accidentally mixed. For a header-only library this
+# is not required. The check can be disabled by temporarily unsetting
+# CMAKE_SIZEOF_VOID_P. In CMake 3.14 and later this can be achieved more cleanly
+# with write_basic_package_version_file(ARCH_INDEPENDENT).
+# TODO: Use this once a newer CMake can be required.
+set(DOCTEST_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
+unset(CMAKE_SIZEOF_VOID_P)
+write_basic_package_version_file(
+ "${version_config}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion
+)
+set(CMAKE_SIZEOF_VOID_P ${DOCTEST_SIZEOF_VOID_P})
+
+configure_file("scripts/cmake/Config.cmake.in" "${project_config}" @ONLY)
+
+if(NOT ${DOCTEST_NO_INSTALL})
+ install(
+ TARGETS ${PROJECT_NAME}
+ EXPORT "${targets_export_name}"
+ INCLUDES DESTINATION "${include_install_dir}"
+ )
+
+ install(
+ FILES "doctest/doctest.h"
+ DESTINATION "${include_install_dir}/doctest"
+ )
+ install(
+ DIRECTORY "doctest/extensions"
+ DESTINATION "${include_install_dir}/doctest"
+ FILES_MATCHING PATTERN "*.h"
+ )
+
+ install(
+ FILES "${project_config}" "${version_config}"
+ DESTINATION "${config_install_dir}"
+ )
+
+ install(
+ FILES "scripts/cmake/doctest.cmake" "scripts/cmake/doctestAddTests.cmake"
+ DESTINATION "${config_install_dir}"
+ )
+
+ install(
+ EXPORT "${targets_export_name}"
+ NAMESPACE "${namespace}"
+ DESTINATION "${config_install_dir}"
+ )
+endif()
diff --git a/contrib/doctest/doctest/doctest.h b/contrib/doctest/doctest/doctest.h
index 42eb03997..5c754cde0 100644
--- a/contrib/doctest/doctest/doctest.h
+++ b/contrib/doctest/doctest/doctest.h
@@ -4,14 +4,14 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2021 Viktor Kirilov
+// Copyright (c) 2016-2023 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT
//
// The documentation can be found at the library's page:
-// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
+// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
//
// =================================================================================================
// =================================================================================================
@@ -48,8 +48,16 @@
#define DOCTEST_VERSION_MAJOR 2
#define DOCTEST_VERSION_MINOR 4
-#define DOCTEST_VERSION_PATCH 6
-#define DOCTEST_VERSION_STR "2.4.6"
+#define DOCTEST_VERSION_PATCH 11
+
+// util we need here
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+
+#define DOCTEST_VERSION_STR \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH)
#define DOCTEST_VERSION \
(DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
@@ -60,6 +68,12 @@
// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect
+#ifdef _MSC_VER
+#define DOCTEST_CPLUSPLUS _MSVC_LANG
+#else
+#define DOCTEST_CPLUSPLUS __cplusplus
+#endif
+
#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH))
// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
@@ -71,12 +85,15 @@
DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
#endif // MSVC
#endif // MSVC
-#if defined(__clang__) && defined(__clang_minor__)
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
!defined(__INTEL_COMPILER)
#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
#ifndef DOCTEST_MSVC
#define DOCTEST_MSVC 0
@@ -87,12 +104,15 @@
#ifndef DOCTEST_GCC
#define DOCTEST_GCC 0
#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
// =================================================================================================
// == COMPILER WARNINGS HELPERS ====================================================================
// =================================================================================================
-#if DOCTEST_CLANG
+#if DOCTEST_CLANG && !DOCTEST_ICC
#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
@@ -137,85 +157,94 @@
// == COMPILER WARNINGS ============================================================================
// =================================================================================================
+// both the header and the implementation suppress all of these,
+// so it only makes sense to aggregate them like so
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
+ \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
+ \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ /* these 4 also disabled globally via cmake: */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
+ /* common ones */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
+ /* static analysis */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */
+
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding
-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
-// static analysis
-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr...
-DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
-
-// 4548 - expression before comma has no effect; expected expression with side - effect
-// 4265 - class has virtual functions, but destructor is not virtual
-// 4986 - exception specification does not match previous declaration
-// 4350 - behavior change: 'member1' called instead of 'member2'
-// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
-// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch
-// 4774 - format string expected in argument 'x' is not a string literal
-// 4820 - padding in structs
-
-// only 4 should be disabled globally:
-// - 4514 # unreferenced inline function has been removed
-// - 4571 # SEH related
-// - 4710 # function not inlined
-// - 4711 # function 'x' selected for automatic inline expansion
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
- DOCTEST_MSVC_SUPPRESS_WARNING(4548) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4265) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4986) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4350) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4668) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4365) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4774) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4820) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4625) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4626) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5027) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5026) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4623) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5039) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5045) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5105)
+ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -228,6 +257,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
// MSVC version table:
// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
+// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022)
// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019)
// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017)
// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
@@ -237,6 +267,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+// Universal Windows Platform support
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_WINDOWS_SEH
+#endif // WINAPI_FAMILY
#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
#define DOCTEST_CONFIG_WINDOWS_SEH
#endif // MSVC
@@ -245,7 +279,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
- !defined(__EMSCRIPTEN__)
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
#define DOCTEST_CONFIG_POSIX_SIGNALS
#endif // _WIN32
#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
@@ -253,7 +287,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
-#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
+ || defined(__wasi__)
#define DOCTEST_CONFIG_NO_EXCEPTIONS
#endif // no exceptions
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -268,6 +303,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
#define DOCTEST_CONFIG_IMPLEMENT
#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@@ -295,6 +334,16 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_INTERFACE
#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+// needed for extern template instantiations
+// see https://github.com/fmtlib/fmt/issues/2228
+#if DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL
+#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE
+#else // DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE
+#define DOCTEST_INTERFACE_DEF
+#endif // DOCTEST_MSVC
+
#define DOCTEST_EMPTY
#if DOCTEST_MSVC
@@ -311,18 +360,61 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
#endif
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
#ifndef DOCTEST_NORETURN
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NORETURN
+#else // DOCTEST_MSVC
#define DOCTEST_NORETURN [[noreturn]]
+#endif // DOCTEST_MSVC
#endif // DOCTEST_NORETURN
#ifndef DOCTEST_NOEXCEPT
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NOEXCEPT
+#else // DOCTEST_MSVC
#define DOCTEST_NOEXCEPT noexcept
+#endif // DOCTEST_MSVC
#endif // DOCTEST_NOEXCEPT
+#ifndef DOCTEST_CONSTEXPR
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_CONSTEXPR const
+#define DOCTEST_CONSTEXPR_FUNC inline
+#else // DOCTEST_MSVC
+#define DOCTEST_CONSTEXPR constexpr
+#define DOCTEST_CONSTEXPR_FUNC constexpr
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_CONSTEXPR
+
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
// =================================================================================================
+#define DOCTEST_DECLARE_INTERFACE(name) \
+ virtual ~name(); \
+ name() = default; \
+ name(const name&) = delete; \
+ name(name&&) = delete; \
+ name& operator=(const name&) = delete; \
+ name& operator=(name&&) = delete;
+
+#define DOCTEST_DEFINE_INTERFACE(name) \
+ name::~name() = default;
+
// internal macros for string concatenation and anonymous variable name generation
#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
@@ -332,8 +424,6 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
#endif // __COUNTER__
-#define DOCTEST_TOSTR(x) #x
-
#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
#define DOCTEST_REF_WRAP(x) x&
#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
@@ -347,31 +437,39 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_PLATFORM_IPHONE
#elif defined(_WIN32)
#define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
#else // DOCTEST_PLATFORM
#define DOCTEST_PLATFORM_LINUX
#endif // DOCTEST_PLATFORM
-#define DOCTEST_GLOBAL_NO_WARNINGS(var) \
- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
- DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \
- static const int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
-#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP
+namespace doctest { namespace detail {
+ static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; }
+}}
+
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
#ifndef DOCTEST_BREAK_INTO_DEBUGGER
// should probably take a look at https://github.com/scottt/debugbreak
#ifdef DOCTEST_PLATFORM_LINUX
#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
// Break at the location of the failing check if possible
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
#else
#include <signal.h>
#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
#endif
#elif defined(DOCTEST_PLATFORM_MAC)
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#elif defined(__ppc__) || defined(__ppc64__)
+// https://www.cocoawithlove.com/2008/03/break-into-debugger.html
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler)
#else
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler)
#endif
#elif DOCTEST_MSVC
#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
@@ -387,54 +485,67 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
// this is kept here for backwards compatibility since the config option was changed
#ifdef DOCTEST_CONFIG_USE_IOSFWD
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
#endif // DOCTEST_CONFIG_USE_IOSFWD
+// for clang - always include ciso646 (which drags some std stuff) because
+// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in
+// which case we don't want to forward declare stuff from std - for reference:
+// https://github.com/doctest/doctest/issues/126
+// https://github.com/doctest/doctest/issues/356
+#if DOCTEST_CLANG
+#include <ciso646>
+#endif // clang
+
+#ifdef _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // _LIBCPP_VERSION
+
#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
-#include <iosfwd>
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <cstddef>
#include <ostream>
+#include <istream>
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#else // DOCTEST_CONFIG_USE_STD_HEADERS
-#if DOCTEST_CLANG
-// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier)
-#include <ciso646>
-#endif // clang
-
-#ifdef _LIBCPP_VERSION
-#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD
-#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD
-#else // _LIBCPP_VERSION
-#define DOCTEST_STD_NAMESPACE_BEGIN namespace std {
-#define DOCTEST_STD_NAMESPACE_END }
-#endif // _LIBCPP_VERSION
-
// Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
-DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp)
-typedef decltype(nullptr) nullptr_t;
+namespace std { // NOLINT(cert-dcl58-cpp)
+typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using)
+typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using)
template <class charT>
struct char_traits;
template <>
struct char_traits<char>;
template <class charT, class traits>
-class basic_ostream;
-typedef basic_ostream<char, char_traits<char>> ostream;
+class basic_ostream; // NOLINT(fuchsia-virtual-inheritance)
+typedef basic_ostream<char, char_traits<char>> ostream; // NOLINT(modernize-use-using)
+template<class traits>
+// NOLINTNEXTLINE
+basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>&, const char*);
+template <class charT, class traits>
+class basic_istream;
+typedef basic_istream<char, char_traits<char>> istream; // NOLINT(modernize-use-using)
template <class... Types>
class tuple;
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
-template <class _Ty>
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+template <class Ty>
class allocator;
-template <class _Elem, class _Traits, class _Alloc>
+template <class Elem, class Traits, class Alloc>
class basic_string;
using string = basic_string<char, char_traits<char>, allocator<char>>;
#endif // VS 2019
-DOCTEST_STD_NAMESPACE_END
+} // namespace std
DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -446,8 +557,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
namespace doctest {
+using std::size_t;
+
DOCTEST_INTERFACE extern bool is_running_in_test;
+#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE
+#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned
+#endif
+
// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
@@ -460,7 +577,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// TODO:
// - optimizations - like not deleting memory unnecessarily in operator= and etc.
// - resize/reserve/clear
-// - substr
// - replace
// - back/front
// - iterator stuff
@@ -470,63 +586,84 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// - relational operators as free functions - taking const char* as one of the params
class DOCTEST_INTERFACE String
{
- static const unsigned len = 24; //!OCLINT avoid private static members
- static const unsigned last = len - 1; //!OCLINT avoid private static members
+public:
+ using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE;
+
+private:
+ static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members
+ static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members
struct view // len should be more than sizeof(view) - because of the final byte for flags
{
char* ptr;
- unsigned size;
- unsigned capacity;
+ size_type size;
+ size_type capacity;
};
union
{
- char buf[len];
+ char buf[len]; // NOLINT(*-avoid-c-arrays)
view data;
};
- bool isOnStack() const { return (buf[last] & 128) == 0; }
- void setOnHeap();
- void setLast(unsigned in = last);
+ char* allocate(size_type sz);
+
+ bool isOnStack() const noexcept { return (buf[last] & 128) == 0; }
+ void setOnHeap() noexcept;
+ void setLast(size_type in = last) noexcept;
+ void setSize(size_type sz) noexcept;
void copy(const String& other);
public:
- String();
+ static DOCTEST_CONSTEXPR size_type npos = static_cast<size_type>(-1);
+
+ String() noexcept;
~String();
// cppcheck-suppress noExplicitConstructor
String(const char* in);
- String(const char* in, unsigned in_size);
+ String(const char* in, size_type in_size);
+
+ String(std::istream& in, size_type in_size);
String(const String& other);
String& operator=(const String& other);
String& operator+=(const String& other);
- String operator+(const String& other) const;
- String(String&& other);
- String& operator=(String&& other);
+ String(String&& other) noexcept;
+ String& operator=(String&& other) noexcept;
- char operator[](unsigned i) const;
- char& operator[](unsigned i);
+ char operator[](size_type i) const;
+ char& operator[](size_type i);
// the only functions I'm willing to leave in the interface - available for inlining
const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT
char* c_str() {
- if(isOnStack())
+ if (isOnStack()) {
return reinterpret_cast<char*>(buf);
+ }
return data.ptr;
}
- unsigned size() const;
- unsigned capacity() const;
+ size_type size() const;
+ size_type capacity() const;
+
+ String substr(size_type pos, size_type cnt = npos) &&;
+ String substr(size_type pos, size_type cnt = npos) const &;
+
+ size_type find(char ch, size_type pos = 0) const;
+ size_type rfind(char ch, size_type pos = npos) const;
int compare(const char* other, bool no_case = false) const;
int compare(const String& other, bool no_case = false) const;
+
+friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
};
+DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
+
DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs);
@@ -534,7 +671,21 @@ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs);
-DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
+class DOCTEST_INTERFACE Contains {
+public:
+ explicit Contains(const String& string);
+
+ bool checkWith(const String& other) const;
+
+ String string;
+};
+
+DOCTEST_INTERFACE String toString(const Contains& in);
+
+DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs);
namespace Color {
enum Enum
@@ -607,7 +758,7 @@ namespace assertType {
DT_WARN_THROWS_WITH = is_throws_with | is_warn,
DT_CHECK_THROWS_WITH = is_throws_with | is_check,
DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
-
+
DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
@@ -688,9 +839,27 @@ struct DOCTEST_INTERFACE AssertData
String m_decomp;
// for specific exception-related asserts
- bool m_threw_as;
- const char* m_exception_type;
- const char* m_exception_string;
+ bool m_threw_as;
+ const char* m_exception_type;
+
+ class DOCTEST_INTERFACE StringContains {
+ private:
+ Contains content;
+ bool isContains;
+
+ public:
+ StringContains(const String& str) : content(str), isContains(false) { }
+ StringContains(Contains cntn) : content(static_cast<Contains&&>(cntn)), isContains(true) { }
+
+ bool check(const String& str) { return isContains ? (content == str) : (content.string == str); }
+
+ operator const String&() const { return content.string; }
+
+ const char* c_str() const { return content.string.c_str(); }
+ } m_exception_string;
+
+ AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string);
};
struct DOCTEST_INTERFACE MessageData
@@ -707,13 +876,13 @@ struct DOCTEST_INTERFACE SubcaseSignature
const char* m_file;
int m_line;
+ bool operator==(const SubcaseSignature& other) const;
bool operator<(const SubcaseSignature& other) const;
};
struct DOCTEST_INTERFACE IContextScope
{
- IContextScope();
- virtual ~IContextScope();
+ DOCTEST_DECLARE_INTERFACE(IContextScope)
virtual void stringify(std::ostream*) const = 0;
};
@@ -723,9 +892,8 @@ namespace detail {
struct ContextOptions //!OCLINT too many fields
{
- std::ostream* cout; // stdout stream - std::cout by default
- std::ostream* cerr; // stderr stream - std::cerr by default
- String binary_name; // the test binary name
+ std::ostream* cout = nullptr; // stdout stream
+ String binary_name; // the test binary name
const detail::TestCase* currentTest = nullptr;
@@ -744,9 +912,12 @@ struct ContextOptions //!OCLINT too many fields
bool case_sensitive; // if filtering should be case sensitive
bool exit; // if the program should be exited after the tests are ran/whatever
bool duration; // print the time duration of each test case
+ bool minimal; // minimal console output (only test failures)
+ bool quiet; // no console output
bool no_throw; // to skip exceptions-related assertion macros
bool no_exitcode; // if the framework should return 0 as the exitcode
bool no_run; // to not run the tests at all (can be done with an "*" exclude)
+ bool no_intro; // to not print the intro of the framework
bool no_version; // to not print the version of the framework
bool no_colors; // if output to the console should be colorized
bool force_colors; // forces the use of colors even when a tty cannot be detected
@@ -768,150 +939,189 @@ struct ContextOptions //!OCLINT too many fields
};
namespace detail {
- template <bool CONDITION, typename TYPE = void>
- struct enable_if
- {};
+ namespace types {
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ using namespace std;
+#else
+ template <bool COND, typename T = void>
+ struct enable_if { };
- template <typename TYPE>
- struct enable_if<true, TYPE>
- { typedef TYPE type; };
+ template <typename T>
+ struct enable_if<true, T> { using type = T; };
- // clang-format off
- template<class T> struct remove_reference { typedef T type; };
- template<class T> struct remove_reference<T&> { typedef T type; };
- template<class T> struct remove_reference<T&&> { typedef T type; };
+ struct true_type { static DOCTEST_CONSTEXPR bool value = true; };
+ struct false_type { static DOCTEST_CONSTEXPR bool value = false; };
+
+ template <typename T> struct remove_reference { using type = T; };
+ template <typename T> struct remove_reference<T&> { using type = T; };
+ template <typename T> struct remove_reference<T&&> { using type = T; };
+
+ template <typename T> struct is_rvalue_reference : false_type { };
+ template <typename T> struct is_rvalue_reference<T&&> : true_type { };
- template<typename T, typename U = T&&> U declval(int);
+ template<typename T> struct remove_const { using type = T; };
+ template <typename T> struct remove_const<const T> { using type = T; };
- template<typename T> T declval(long);
+ // Compiler intrinsics
+ template <typename T> struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); };
+ template <typename T> struct underlying_type { using type = __underlying_type(T); };
- template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ;
+ template <typename T> struct is_pointer : false_type { };
+ template <typename T> struct is_pointer<T*> : true_type { };
- template<class T> struct is_lvalue_reference { const static bool value=false; };
- template<class T> struct is_lvalue_reference<T&> { const static bool value=true; };
+ template <typename T> struct is_array : false_type { };
+ // NOLINTNEXTLINE(*-avoid-c-arrays)
+ template <typename T, size_t SIZE> struct is_array<T[SIZE]> : true_type { };
+#endif
+ }
+
+ // <utility>
+ template <typename T>
+ T&& declval();
template <class T>
- inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT
- {
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type& t) DOCTEST_NOEXCEPT {
return static_cast<T&&>(t);
}
template <class T>
- inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT
- {
- static_assert(!is_lvalue_reference<T>::value,
- "Can not forward an rvalue as an lvalue.");
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type&& t) DOCTEST_NOEXCEPT {
return static_cast<T&&>(t);
}
- template<class T> struct remove_const { typedef T type; };
- template<class T> struct remove_const<const T> { typedef T type; };
-#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
- template<class T> struct is_enum : public std::is_enum<T> {};
- template<class T> struct underlying_type : public std::underlying_type<T> {};
-#else
- // Use compiler intrinsics
- template<class T> struct is_enum { constexpr static bool value = __is_enum(T); };
- template<class T> struct underlying_type { typedef __underlying_type(T) type; };
-#endif
- // clang-format on
+ template <typename T>
+ struct deferred_false : types::false_type { };
+
+// MSVS 2015 :(
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
+ template <typename T, typename = void>
+ struct has_global_insertion_operator : types::false_type { };
template <typename T>
- struct deferred_false
- // cppcheck-suppress unusedStructMember
- { static const bool value = false; };
-
- namespace has_insertion_operator_impl {
- std::ostream &os();
- template<class T>
- DOCTEST_REF_WRAP(T) val();
-
- template<class, class = void>
- struct check {
- static constexpr bool value = false;
- };
+ struct has_global_insertion_operator<T, decltype(::operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
- template<class T>
- struct check<T, decltype(os() << val<T>(), void())> {
- static constexpr bool value = true;
- };
- } // namespace has_insertion_operator_impl
+ template <typename T, typename = void>
+ struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator<T>::value; };
+
+ template <typename T, bool global>
+ struct insert_hack;
+
+ template <typename T>
+ struct insert_hack<T, true> {
+ static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); }
+ };
+
+ template <typename T>
+ struct insert_hack<T, false> {
+ static void insert(std::ostream& os, const T& t) { operator<<(os, t); }
+ };
+
+ template <typename T>
+ using insert_hack_t = insert_hack<T, has_global_insertion_operator<T>::value>;
+#else
+ template <typename T, typename = void>
+ struct has_insertion_operator : types::false_type { };
+#endif
- template<class T>
- using has_insertion_operator = has_insertion_operator_impl::check<const T>;
+ template <typename T>
+ struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
- DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num);
+ template <typename T>
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum<T>::value && !doctest::detail::has_insertion_operator<T>::value;
+ };
- DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream
- DOCTEST_INTERFACE String getTlsOssResult();
+ DOCTEST_INTERFACE std::ostream* tlssPush();
+ DOCTEST_INTERFACE String tlssPop();
template <bool C>
- struct StringMakerBase
- {
+ struct StringMakerBase {
template <typename T>
static String convert(const DOCTEST_REF_WRAP(T)) {
+#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES
+ static_assert(deferred_false<T>::value, "No stringification detected for type T. See string conversion manual");
+#endif
return "{?}";
}
};
- template <>
- struct StringMakerBase<true>
- {
- template <typename T>
- static String convert(const DOCTEST_REF_WRAP(T) in) {
- *getTlsOss() << in;
- return getTlsOssResult();
- }
- };
-
- DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size);
+ template <typename T>
+ struct filldata;
template <typename T>
- String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) {
- return rawMemoryToString(&object, sizeof(object));
+ void filloss(std::ostream* stream, const T& in) {
+ filldata<T>::fill(stream, in);
+ }
+
+ template <typename T, size_t N>
+ void filloss(std::ostream* stream, const T (&in)[N]) { // NOLINT(*-avoid-c-arrays)
+ // T[N], T(&)[N], T(&&)[N] have same behaviour.
+ // Hence remove reference.
+ filloss<typename types::remove_reference<decltype(in)>::type>(stream, in);
}
template <typename T>
- const char* type_to_string() {
- return "<>";
+ String toStream(const T& in) {
+ std::ostream* stream = tlssPush();
+ filloss(stream, in);
+ return tlssPop();
}
+
+ template <>
+ struct StringMakerBase<true> {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+ return toStream(in);
+ }
+ };
} // namespace detail
template <typename T>
-struct StringMaker : public detail::StringMakerBase<detail::has_insertion_operator<T>::value>
+struct StringMaker : public detail::StringMakerBase<
+ detail::has_insertion_operator<T>::value || detail::types::is_pointer<T>::value || detail::types::is_array<T>::value>
{};
-template <typename T>
-struct StringMaker<T*>
-{
- template <typename U>
- static String convert(U* p) {
- if(p)
- return detail::rawMemoryToString(p);
- return "NULL";
- }
-};
+#ifndef DOCTEST_STRINGIFY
+#ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY
+#define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__))
+#else
+#define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__)
+#endif
+#endif
-template <typename R, typename C>
-struct StringMaker<R C::*>
-{
- static String convert(R C::*p) {
- if(p)
- return detail::rawMemoryToString(p);
- return "NULL";
- }
-};
+template <typename T>
+String toString() {
+#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0
+ String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void)
+ String::size_type beginPos = ret.find('<');
+ return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(sizeof(">(void)")));
+#else
+ String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE]
+ String::size_type begin = ret.find('=') + 2;
+ return ret.substr(begin, ret.size() - begin - 1);
+#endif
+}
-template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true>
+template <typename T, typename detail::types::enable_if<!detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
return StringMaker<T>::convert(value);
}
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-DOCTEST_INTERFACE String toString(char* in);
DOCTEST_INTERFACE String toString(const char* in);
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+DOCTEST_INTERFACE String toString(const std::string& in);
+#endif // VS 2019
+
+DOCTEST_INTERFACE String toString(String in);
+
+DOCTEST_INTERFACE String toString(std::nullptr_t);
+
DOCTEST_INTERFACE String toString(bool in);
+
DOCTEST_INTERFACE String toString(float in);
DOCTEST_INTERFACE String toString(double in);
DOCTEST_INTERFACE String toString(double long in);
@@ -919,40 +1129,95 @@ DOCTEST_INTERFACE String toString(double long in);
DOCTEST_INTERFACE String toString(char in);
DOCTEST_INTERFACE String toString(char signed in);
DOCTEST_INTERFACE String toString(char unsigned in);
-DOCTEST_INTERFACE String toString(int short in);
-DOCTEST_INTERFACE String toString(int short unsigned in);
-DOCTEST_INTERFACE String toString(int in);
-DOCTEST_INTERFACE String toString(int unsigned in);
-DOCTEST_INTERFACE String toString(int long in);
-DOCTEST_INTERFACE String toString(int long unsigned in);
-DOCTEST_INTERFACE String toString(int long long in);
-DOCTEST_INTERFACE String toString(int long long unsigned in);
-DOCTEST_INTERFACE String toString(std::nullptr_t in);
-
-template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true>
+DOCTEST_INTERFACE String toString(short in);
+DOCTEST_INTERFACE String toString(short unsigned in);
+DOCTEST_INTERFACE String toString(signed in);
+DOCTEST_INTERFACE String toString(unsigned in);
+DOCTEST_INTERFACE String toString(long in);
+DOCTEST_INTERFACE String toString(long unsigned in);
+DOCTEST_INTERFACE String toString(long long in);
+DOCTEST_INTERFACE String toString(long long unsigned in);
+
+template <typename T, typename detail::types::enable_if<detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
- typedef typename detail::underlying_type<T>::type UT;
- return toString(static_cast<UT>(value));
+ using UT = typename detail::types::underlying_type<T>::type;
+ return (DOCTEST_STRINGIFY(static_cast<UT>(value)));
}
-#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
-DOCTEST_INTERFACE String toString(const std::string& in);
-#endif // VS 2019
+namespace detail {
+ template <typename T>
+ struct filldata
+ {
+ static void fill(std::ostream* stream, const T& in) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ insert_hack_t<T>::insert(*stream, in);
+#else
+ operator<<(*stream, in);
+#endif
+ }
+ };
+
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+// NOLINTBEGIN(*-avoid-c-arrays)
+ template <typename T, size_t N>
+ struct filldata<T[N]> {
+ static void fill(std::ostream* stream, const T(&in)[N]) {
+ *stream << "[";
+ for (size_t i = 0; i < N; i++) {
+ if (i != 0) { *stream << ", "; }
+ *stream << (DOCTEST_STRINGIFY(in[i]));
+ }
+ *stream << "]";
+ }
+ };
+// NOLINTEND(*-avoid-c-arrays)
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ // Specialized since we don't want the terminating null byte!
+// NOLINTBEGIN(*-avoid-c-arrays)
+ template <size_t N>
+ struct filldata<const char[N]> {
+ static void fill(std::ostream* stream, const char (&in)[N]) {
+ *stream << String(in, in[N - 1] ? N : N - 1);
+ } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+ };
+// NOLINTEND(*-avoid-c-arrays)
+
+ template <>
+ struct filldata<const void*> {
+ static void fill(std::ostream* stream, const void* in);
+ };
+
+ template <typename T>
+ struct filldata<T*> {
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180)
+ static void fill(std::ostream* stream, const T* in) {
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast")
+ filldata<const void*>::fill(stream,
+#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)
+ reinterpret_cast<const void*>(in)
+#else
+ *reinterpret_cast<const void* const*>(&in)
+#endif
+ );
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ }
+ };
+}
-class DOCTEST_INTERFACE Approx
+struct DOCTEST_INTERFACE Approx
{
-public:
- explicit Approx(double value);
+ Approx(double value);
Approx operator()(double value) const;
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
explicit Approx(const T& value,
- typename detail::enable_if<std::is_constructible<double, T>::value>::type* =
+ typename detail::types::enable_if<std::is_constructible<double, T>::value>::type* =
static_cast<T*>(nullptr)) {
- *this = Approx(static_cast<double>(value));
+ *this = static_cast<double>(value);
}
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
@@ -960,7 +1225,7 @@ public:
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
- typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
const T& newEpsilon) {
m_epsilon = static_cast<double>(newEpsilon);
return *this;
@@ -971,7 +1236,7 @@ public:
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
- typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
const T& newScale) {
m_scale = static_cast<double>(newScale);
return *this;
@@ -992,30 +1257,27 @@ public:
DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs);
DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs);
- DOCTEST_INTERFACE friend String toString(const Approx& in);
-
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#define DOCTEST_APPROX_PREFIX \
- template <typename T> friend typename detail::enable_if<std::is_constructible<double, T>::value, bool>::type
+ template <typename T> friend typename std::enable_if<std::is_constructible<double, T>::value, bool>::type
- DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); }
+ DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast<double>(lhs), rhs); }
DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
- DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) && lhs != rhs; }
#undef DOCTEST_APPROX_PREFIX
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
// clang-format on
-private:
double m_epsilon;
double m_scale;
double m_value;
@@ -1025,18 +1287,35 @@ DOCTEST_INTERFACE String toString(const Approx& in);
DOCTEST_INTERFACE const ContextOptions* getContextOptions();
-#if !defined(DOCTEST_CONFIG_DISABLE)
+template <typename F>
+struct DOCTEST_INTERFACE_DECL IsNaN
+{
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN<F> operator!() const { return { value, !flipped }; }
+ operator bool() const;
+};
+#ifndef __MINGW32__
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<float>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<double>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<long double>;
+#endif
+DOCTEST_INTERFACE String toString(IsNaN<float> in);
+DOCTEST_INTERFACE String toString(IsNaN<double> in);
+DOCTEST_INTERFACE String toString(IsNaN<double long> in);
+
+#ifndef DOCTEST_CONFIG_DISABLE
namespace detail {
// clang-format off
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- template<class T> struct decay_array { typedef T type; };
- template<class T, unsigned N> struct decay_array<T[N]> { typedef T* type; };
- template<class T> struct decay_array<T[]> { typedef T* type; };
+ template<class T> struct decay_array { using type = T; };
+ template<class T, unsigned N> struct decay_array<T[N]> { using type = T*; };
+ template<class T> struct decay_array<T[]> { using type = T*; };
- template<class T> struct not_char_pointer { enum { value = 1 }; };
- template<> struct not_char_pointer<char*> { enum { value = 0 }; };
- template<> struct not_char_pointer<const char*> { enum { value = 0 }; };
+ template<class T> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; };
+ template<> struct not_char_pointer<char*> { static DOCTEST_CONSTEXPR int value = 0; };
+ template<> struct not_char_pointer<const char*> { static DOCTEST_CONSTEXPR int value = 0; };
template<class T> struct can_use_op : public not_char_pointer<typename decay_array<T>::type> {};
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
@@ -1059,16 +1338,22 @@ namespace detail {
bool m_entered = false;
Subcase(const String& name, const char* file, int line);
+ Subcase(const Subcase&) = delete;
+ Subcase(Subcase&&) = delete;
+ Subcase& operator=(const Subcase&) = delete;
+ Subcase& operator=(Subcase&&) = delete;
~Subcase();
operator bool() const;
+
+ private:
+ bool checkFilters();
};
template <typename L, typename R>
String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
const DOCTEST_REF_WRAP(R) rhs) {
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
- return toString(lhs) + op + toString(rhs);
+ return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs));
}
#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
@@ -1079,12 +1364,16 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
// If not it doesn't find the operator or if the operator at global scope is defined after
// this template, the template won't be instantiated due to SFINAE. Once the template is not
// instantiated it can look for global operator using normal conversions.
-#define SFINAE_OP(ret,op) decltype(doctest::detail::declval<L>() op doctest::detail::declval<R>(),static_cast<ret>(0))
+#ifdef __NVCC__
+#define SFINAE_OP(ret,op) ret
+#else
+#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{})
+#endif
#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
template <typename R> \
- DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
- bool res = op_macro(doctest::detail::forward<L>(lhs), doctest::detail::forward<R>(rhs)); \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward<const L>(lhs), doctest::detail::forward<R>(rhs)); \
if(m_at & assertType::is_false) \
res = !res; \
if(!res || doctest::getContextOptions()->success) \
@@ -1103,11 +1392,12 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
return *this; \
}
- struct DOCTEST_INTERFACE Result
+ struct DOCTEST_INTERFACE Result // NOLINT(*-member-init)
{
bool m_passed;
String m_decomp;
+ Result() = default; // TODO: Why do we need this? (To remove NOLINT)
Result(bool passed, const String& decomposition = String());
// forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
@@ -1164,8 +1454,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#define DOCTEST_COMPARISON_RETURN_TYPE bool
#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
@@ -1213,26 +1502,26 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
assertType::Enum m_at;
explicit Expression_lhs(L&& in, assertType::Enum at)
- : lhs(doctest::detail::forward<L>(in))
+ : lhs(static_cast<L&&>(in))
, m_at(at) {}
DOCTEST_NOINLINE operator Result() {
-// this is needed only foc MSVC 2015:
-// https://ci.appveyor.com/project/onqtam/doctest/builds/38181202
+// this is needed only for MSVC 2015
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
bool res = static_cast<bool>(lhs);
DOCTEST_MSVC_SUPPRESS_WARNING_POP
- if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ if(m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
res = !res;
+ }
- if(!res || getContextOptions()->success)
- return Result(res, toString(lhs));
- return Result(res);
+ if(!res || getContextOptions()->success) {
+ return { res, (DOCTEST_STRINGIFY(lhs)) };
+ }
+ return { res };
}
- /* This is required for user-defined conversions from Expression_lhs to L */
- //operator L() const { return lhs; }
- operator L() const { return lhs; }
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ operator L() const { return lhs; }
// clang-format off
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
@@ -1289,22 +1578,27 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// https://github.com/catchorg/Catch2/issues/870
// https://github.com/catchorg/Catch2/issues/565
template <typename L>
- Expression_lhs<L> operator<<(L &&operand) {
- return Expression_lhs<L>(doctest::detail::forward<L>(operand), m_at);
+ Expression_lhs<L> operator<<(L&& operand) {
+ return Expression_lhs<L>(static_cast<L&&>(operand), m_at);
+ }
+
+ template <typename L,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<L>::value,void >::type* = nullptr>
+ Expression_lhs<const L&> operator<<(const L &operand) {
+ return Expression_lhs<const L&>(operand, m_at);
}
};
struct DOCTEST_INTERFACE TestSuite
{
- const char* m_test_suite;
- const char* m_description;
- bool m_skip;
- bool m_no_breaks;
- bool m_no_output;
- bool m_may_fail;
- bool m_should_fail;
- int m_expected_failures;
- double m_timeout;
+ const char* m_test_suite = nullptr;
+ const char* m_description = nullptr;
+ bool m_skip = false;
+ bool m_no_breaks = false;
+ bool m_no_output = false;
+ bool m_may_fail = false;
+ bool m_should_fail = false;
+ int m_expected_failures = 0;
+ double m_timeout = 0;
TestSuite& operator*(const char* in);
@@ -1315,25 +1609,28 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
}
};
- typedef void (*funcType)();
+ using funcType = void (*)();
struct DOCTEST_INTERFACE TestCase : public TestCaseData
{
funcType m_test; // a function pointer to the test case
- const char* m_type; // for templated test cases - gets appended to the real name
+ String m_type; // for templated test cases - gets appended to the real name
int m_template_id; // an ID used to distinguish between the different versions of a templated test case
String m_full_name; // contains the name (only for templated test cases!) + the template type
TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
- const char* type = "", int template_id = -1);
+ const String& type = String(), int template_id = -1);
TestCase(const TestCase& other);
+ TestCase(TestCase&&) = delete;
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
TestCase& operator=(const TestCase& other);
DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ TestCase& operator=(TestCase&&) = delete;
+
TestCase& operator*(const char* in);
template <typename T>
@@ -1343,6 +1640,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
}
bool operator<(const TestCase& other) const;
+
+ ~TestCase() = default;
};
// forward declarations of functions used by the macros
@@ -1382,27 +1681,36 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
struct DOCTEST_INTERFACE ResultBuilder : public AssertData
{
ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
- const char* exception_type = "", const char* exception_string = "");
+ const char* exception_type = "", const String& exception_string = "");
+
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string);
void setResult(const Result& res);
template <int comparison, typename L, typename R>
- DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs,
const DOCTEST_REF_WRAP(R) rhs) {
m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
- if(m_failed || getContextOptions()->success)
+ if (m_failed || getContextOptions()->success) {
m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
+ }
+ return !m_failed;
}
template <typename L>
- DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) {
+ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) {
m_failed = !val;
- if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
m_failed = !m_failed;
+ }
+
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = (DOCTEST_STRINGIFY(val));
+ }
- if(m_failed || getContextOptions()->success)
- m_decomp = toString(val);
+ return !m_failed;
}
void translateException();
@@ -1422,8 +1730,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad);
- DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line,
- const char* expr, Result result);
+ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const Result& result);
#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \
do { \
@@ -1438,7 +1746,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
if(checkIfShouldThrow(at)) \
throwException(); \
} \
- return; \
+ return !failed; \
} \
} while(false)
@@ -1453,7 +1761,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
throwException()
template <int comparison, typename L, typename R>
- DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line,
+ DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line,
const char* expr, const DOCTEST_REF_WRAP(L) lhs,
const DOCTEST_REF_WRAP(R) rhs) {
bool failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
@@ -1464,10 +1772,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// ###################################################################################
DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ return !failed;
}
template <typename L>
- DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line,
+ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line,
const char* expr, const DOCTEST_REF_WRAP(L) val) {
bool failed = !val;
@@ -1478,14 +1787,14 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
// THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
// ###################################################################################
- DOCTEST_ASSERT_OUT_OF_TESTS(toString(val));
- DOCTEST_ASSERT_IN_TESTS(toString(val));
+ DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val)));
+ DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val)));
+ return !failed;
}
struct DOCTEST_INTERFACE IExceptionTranslator
{
- IExceptionTranslator();
- virtual ~IExceptionTranslator();
+ DOCTEST_DECLARE_INTERFACE(IExceptionTranslator)
virtual bool translate(String&) const = 0;
};
@@ -1501,7 +1810,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
try {
throw; // lgtm [cpp/rethrow-no-exception]
// cppcheck-suppress catchExceptionByValue
- } catch(T ex) { // NOLINT
+ } catch(const T& ex) {
res = m_translateFunction(ex); //!OCLINT parameter reassignment
return true;
} catch(...) {} //!OCLINT - empty catch statement
@@ -1516,95 +1825,70 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et);
- template <bool C>
- struct StringStreamBase
- {
- template <typename T>
- static void convert(std::ostream* s, const T& in) {
- *s << toString(in);
- }
-
- // always treat char* as a string in this context - no matter
- // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined
- static void convert(std::ostream* s, const char* in) { *s << String(in); }
- };
-
- template <>
- struct StringStreamBase<true>
- {
- template <typename T>
- static void convert(std::ostream* s, const T& in) {
- *s << in;
- }
- };
+ // ContextScope base class used to allow implementing methods of ContextScope
+ // that don't depend on the template parameter in doctest.cpp.
+ struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
+ ContextScopeBase(const ContextScopeBase&) = delete;
- template <typename T>
- struct StringStream : public StringStreamBase<has_insertion_operator<T>::value>
- {};
+ ContextScopeBase& operator=(const ContextScopeBase&) = delete;
+ ContextScopeBase& operator=(ContextScopeBase&&) = delete;
- template <typename T>
- void toStream(std::ostream* s, const T& value) {
- StringStream<T>::convert(s, value);
- }
+ ~ContextScopeBase() override = default;
-#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- DOCTEST_INTERFACE void toStream(std::ostream* s, char* in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in);
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- DOCTEST_INTERFACE void toStream(std::ostream* s, bool in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, float in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, double in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, double long in);
-
- DOCTEST_INTERFACE void toStream(std::ostream* s, char in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int short in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in);
-
- // ContextScope base class used to allow implementing methods of ContextScope
- // that don't depend on the template parameter in doctest.cpp.
- class DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
protected:
ContextScopeBase();
+ ContextScopeBase(ContextScopeBase&& other) noexcept;
void destroy();
+ bool need_to_destroy{true};
};
template <typename L> class ContextScope : public ContextScopeBase
{
- const L lambda_;
+ L lambda_;
public:
explicit ContextScope(const L &lambda) : lambda_(lambda) {}
+ explicit ContextScope(L&& lambda) : lambda_(static_cast<L&&>(lambda)) { }
- ContextScope(ContextScope &&other) : lambda_(other.lambda_) {}
+ ContextScope(const ContextScope&) = delete;
+ ContextScope(ContextScope&&) noexcept = default;
+
+ ContextScope& operator=(const ContextScope&) = delete;
+ ContextScope& operator=(ContextScope&&) = delete;
void stringify(std::ostream* s) const override { lambda_(s); }
- ~ContextScope() override { destroy(); }
+ ~ContextScope() override {
+ if (need_to_destroy) {
+ destroy();
+ }
+ }
};
struct DOCTEST_INTERFACE MessageBuilder : public MessageData
{
std::ostream* m_stream;
+ bool logged = false;
MessageBuilder(const char* file, int line, assertType::Enum severity);
- MessageBuilder() = delete;
+
+ MessageBuilder(const MessageBuilder&) = delete;
+ MessageBuilder(MessageBuilder&&) = delete;
+
+ MessageBuilder& operator=(const MessageBuilder&) = delete;
+ MessageBuilder& operator=(MessageBuilder&&) = delete;
+
~MessageBuilder();
// the preferred way of chaining parameters for stringification
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
template <typename T>
MessageBuilder& operator,(const T& in) {
- toStream(m_stream, in);
+ *m_stream << (DOCTEST_STRINGIFY(in));
return *this;
}
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
// kept here just for backwards-compatibility - the comma operator should be preferred now
template <typename T>
@@ -1620,7 +1904,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
bool log();
void react();
};
-
+
template <typename L>
ContextScope<L> MakeContextScope(const L &lambda) {
return ContextScope<L>(lambda);
@@ -1673,7 +1957,7 @@ int registerExceptionTranslator(String (*)(T)) {
#endif // DOCTEST_CONFIG_DISABLE
namespace detail {
- typedef void (*assert_handler)(const AssertData&);
+ using assert_handler = void (*)(const AssertData&);
struct ContextState;
} // namespace detail
@@ -1686,12 +1970,19 @@ class DOCTEST_INTERFACE Context
public:
explicit Context(int argc = 0, const char* const* argv = nullptr);
- ~Context();
+ Context(const Context&) = delete;
+ Context(Context&&) = delete;
+
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ ~Context(); // NOLINT(performance-trivially-destructible)
void applyCommandLine(int argc, const char* const* argv);
void addFilter(const char* filter, const char* value);
void clearFilters();
+ void setOption(const char* option, bool value);
void setOption(const char* option, int value);
void setOption(const char* option, const char* value);
@@ -1701,6 +1992,8 @@ public:
void setAssertHandler(detail::assert_handler ah);
+ void setCout(std::ostream* out);
+
int run();
};
@@ -1727,6 +2020,7 @@ struct DOCTEST_INTERFACE CurrentTestCaseStats
int numAssertsFailedCurrentTest;
double seconds;
int failure_flags; // use TestCaseFailureReason::Enum
+ bool testCaseSuccess;
};
struct DOCTEST_INTERFACE TestCaseException
@@ -1790,8 +2084,7 @@ struct DOCTEST_INTERFACE IReporter
// or isn't in the execution range (between first and last) (safe to cache a pointer to the input)
virtual void test_case_skipped(const TestCaseData&) = 0;
- // doctest will not be managing the lifetimes of reporters given to it but this would still be nice to have
- virtual ~IReporter();
+ DOCTEST_DECLARE_INTERFACE(IReporter)
// can obtain all currently active contexts and stringify them if one wishes to do so
static int get_num_active_contexts();
@@ -1803,7 +2096,7 @@ struct DOCTEST_INTERFACE IReporter
};
namespace detail {
- typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&);
+ using reporterCreatorFunc = IReporter* (*)(const ContextOptions&);
DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter);
@@ -1820,14 +2113,30 @@ int registerReporter(const char* name, int priority, bool isReporter) {
}
} // namespace doctest
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_EMPTY [] { return false; }()
+#else
+#define DOCTEST_FUNC_EMPTY (void)0
+#endif
+
// if registering is not disabled
-#if !defined(DOCTEST_CONFIG_DISABLE)
+#ifndef DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_SCOPE_BEGIN [&]
+#define DOCTEST_FUNC_SCOPE_END ()
+#define DOCTEST_FUNC_SCOPE_RET(v) return v
+#else
+#define DOCTEST_FUNC_SCOPE_BEGIN do
+#define DOCTEST_FUNC_SCOPE_END while(false)
+#define DOCTEST_FUNC_SCOPE_RET(v) (void)0
+#endif
// common code in asserts - for convenience
-#define DOCTEST_ASSERT_LOG_AND_REACT(b) \
- if(b.log()) \
- DOCTEST_BREAK_INTO_DEBUGGER(); \
- b.react()
+#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \
+ if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \
+ b.react(); \
+ DOCTEST_FUNC_SCOPE_RET(!b.m_failed)
#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#define DOCTEST_WRAP_IN_TRY(x) x;
@@ -1835,7 +2144,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_WRAP_IN_TRY(x) \
try { \
x; \
- } catch(...) { _DOCTEST_RB.translateException(); }
+ } catch(...) { DOCTEST_RB.translateException(); }
#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
@@ -1849,27 +2158,26 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// registers the test by initializing a dummy var with a function
#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \
- global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
+ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \
doctest::detail::regTest( \
doctest::detail::TestCase( \
f, __FILE__, __LINE__, \
doctest_detail_test_suite_ns::getCurrentTestSuite()) * \
- decorators); \
- DOCTEST_GLOBAL_NO_WARNINGS_END()
+ decorators))
#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \
- namespace { \
+ namespace { /* NOLINT */ \
struct der : public base \
{ \
void f(); \
}; \
- static void func() { \
+ static DOCTEST_INLINE_NOINLINE void func() { \
der v; \
v.f(); \
} \
DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \
} \
- inline DOCTEST_NOINLINE void der::f()
+ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \
static void f(); \
@@ -1878,18 +2186,18 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \
static doctest::detail::funcType proxy() { return f; } \
- DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \
+ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \
static void f()
// for registering tests
#define DOCTEST_TEST_CASE(decorators) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
// for registering tests in classes - requires C++17 for inline variables!
-#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L)
+#if DOCTEST_CPLUSPLUS >= 201703L
#define DOCTEST_TEST_CASE_CLASS(decorators) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \
decorators)
#else // DOCTEST_TEST_CASE_CLASS
#define DOCTEST_TEST_CASE_CLASS(...) \
@@ -1898,26 +2206,25 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// for registering tests with a fixture
#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \
- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
// for converting types to strings without the <typeinfo> header and demangling
-#define DOCTEST_TYPE_TO_STRING_IMPL(...) \
- template <> \
- inline const char* type_to_string<__VA_ARGS__>() { \
- return "<" #__VA_ARGS__ ">"; \
- }
-#define DOCTEST_TYPE_TO_STRING(...) \
- namespace doctest { namespace detail { \
- DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \
+ namespace doctest { \
+ template <> \
+ inline String toString<__VA_ARGS__>() { \
+ return str; \
} \
} \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ static_assert(true, "")
+
+#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__)
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \
template <typename T> \
static void func(); \
- namespace { \
+ namespace { /* NOLINT */ \
template <typename Tuple> \
struct iter; \
template <typename Type, typename... Rest> \
@@ -1926,7 +2233,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
iter(const char* file, unsigned line, int index) { \
doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \
doctest_detail_test_suite_ns::getCurrentTestSuite(), \
- doctest::detail::type_to_string<Type>(), \
+ doctest::toString<Type>(), \
int(line) * 1000 + index) \
* dec); \
iter<std::tuple<Rest...>>(file, line, index + 1); \
@@ -1943,20 +2250,20 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \
DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_))
#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \
- doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\
- DOCTEST_GLOBAL_NO_WARNINGS_END()
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \
+ doctest::detail::instantiationHelper( \
+ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0)))
#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
+ static_assert(true, "")
#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \
+ static_assert(true, "")
#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \
DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \
@@ -1965,17 +2272,17 @@ int registerReporter(const char* name, int priority, bool isReporter) {
static void anon()
#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__)
+ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__)
// for subcases
#define DOCTEST_SUBCASE(name) \
- if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
doctest::detail::Subcase(name, __FILE__, __LINE__))
// for grouping tests in test suites by using code blocks
#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \
namespace ns_name { namespace doctest_detail_test_suite_ns { \
- static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \
+ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
@@ -1995,53 +2302,53 @@ int registerReporter(const char* name, int priority, bool isReporter) {
namespace ns_name
#define DOCTEST_TEST_SUITE(decorators) \
- DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_))
+ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_))
// for starting a testsuite block
#define DOCTEST_TEST_SUITE_BEGIN(decorators) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
- doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \
+ static_assert(true, "")
// for ending a testsuite block
#define DOCTEST_TEST_SUITE_END \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
- doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \
+ using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
// for registering exception translators
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \
inline doctest::String translatorName(signature); \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \
- doctest::registerExceptionTranslator(translatorName); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerExceptionTranslator(translatorName)) \
doctest::String translatorName(signature)
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
- DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \
+ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
signature)
// for registering reporters
#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
- doctest::registerReporter<reporter>(name, priority, true); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, true)) \
+ static_assert(true, "")
// for registering listeners
#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
- doctest::registerReporter<reporter>(name, priority, false); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, false)) \
+ static_assert(true, "")
-// for logging
+// clang-format off
+// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557
#define DOCTEST_INFO(...) \
- DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \
+ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \
+ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \
__VA_ARGS__)
+// clang-format on
#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
[&](std::ostream* s_name) { \
doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
mb_name.m_stream = s_name; \
@@ -2051,16 +2358,18 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
mb * __VA_ARGS__; \
- DOCTEST_ASSERT_LOG_AND_REACT(mb); \
- } while(false)
+ if(mb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ mb.react(); \
+ } DOCTEST_FUNC_SCOPE_END
// clang-format off
-#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
-#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
// clang-format on
#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
@@ -2073,18 +2382,37 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \
doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
- << __VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \
+ << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \
DOCTEST_CLANG_SUPPRESS_WARNING_POP
#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY( \
+ DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
+ __VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
@@ -2098,6 +2426,14 @@ int registerReporter(const char* name, int priority, bool isReporter) {
doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
<< __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
+ doctest::detail::binary_assert<doctest::detail::binaryAssertComparison::comparison>( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
+ #__VA_ARGS__, __VA_ARGS__)
+
#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__)
@@ -2108,51 +2444,83 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
// clang-format off
-#define DOCTEST_WARN_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false)
-#define DOCTEST_CHECK_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false)
-#define DOCTEST_REQUIRE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false)
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false)
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false)
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false)
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
// clang-format on
+#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
+#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
+#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
+#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
+#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
+#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
+#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
+#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
+#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
+#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
+#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
+#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
if(!doctest::getContextOptions()->no_throw) { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #expr, #__VA_ARGS__, message); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
- } catch(const typename doctest::detail::remove_const< \
- typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
- _DOCTEST_RB.translateException(); \
- _DOCTEST_RB.m_threw_as = true; \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+ } catch(const typename doctest::detail::types::remove_const< \
+ typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\
+ DOCTEST_RB.translateException(); \
+ DOCTEST_RB.m_threw_as = true; \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
} \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END
#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
if(!doctest::getContextOptions()->no_throw) { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, expr_str, "", __VA_ARGS__); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
} \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END
#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #__VA_ARGS__); \
try { \
DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
// clang-format off
#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
@@ -2175,166 +2543,23 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } while(false)
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } while(false)
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } while(false)
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } while(false)
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } while(false)
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } while(false)
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
// clang-format on
-#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY( \
- _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
- __VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
-
-#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
-
-#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
- doctest::detail::binary_assert<doctest::detail::binaryAssertComparison::comparison>( \
- doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
-
-#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
- doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
- #__VA_ARGS__, __VA_ARGS__)
-
-#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
-#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
-#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
-#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
-#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
-#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
-#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
-#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
-#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
-#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
-#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
-#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
-#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
-#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
-#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
-#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
-#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
-#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
-
-#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
-#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
-#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
-#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
-#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
-#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
-
-#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
-
-#undef DOCTEST_WARN_THROWS
-#undef DOCTEST_CHECK_THROWS
-#undef DOCTEST_REQUIRE_THROWS
-#undef DOCTEST_WARN_THROWS_AS
-#undef DOCTEST_CHECK_THROWS_AS
-#undef DOCTEST_REQUIRE_THROWS_AS
-#undef DOCTEST_WARN_THROWS_WITH
-#undef DOCTEST_CHECK_THROWS_WITH
-#undef DOCTEST_REQUIRE_THROWS_WITH
-#undef DOCTEST_WARN_THROWS_WITH_AS
-#undef DOCTEST_CHECK_THROWS_WITH_AS
-#undef DOCTEST_REQUIRE_THROWS_WITH_AS
-#undef DOCTEST_WARN_NOTHROW
-#undef DOCTEST_CHECK_NOTHROW
-#undef DOCTEST_REQUIRE_NOTHROW
-
-#undef DOCTEST_WARN_THROWS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_MESSAGE
-#undef DOCTEST_WARN_THROWS_AS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_AS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE
-#undef DOCTEST_WARN_THROWS_WITH_MESSAGE
-#undef DOCTEST_CHECK_THROWS_WITH_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
-#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_WARN_NOTHROW_MESSAGE
-#undef DOCTEST_CHECK_NOTHROW_MESSAGE
-#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE
-
-#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
-#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-
-#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
-#undef DOCTEST_REQUIRE
-#undef DOCTEST_REQUIRE_FALSE
-#undef DOCTEST_REQUIRE_MESSAGE
-#undef DOCTEST_REQUIRE_FALSE_MESSAGE
-#undef DOCTEST_REQUIRE_EQ
-#undef DOCTEST_REQUIRE_NE
-#undef DOCTEST_REQUIRE_GT
-#undef DOCTEST_REQUIRE_LT
-#undef DOCTEST_REQUIRE_GE
-#undef DOCTEST_REQUIRE_LE
-#undef DOCTEST_REQUIRE_UNARY
-#undef DOCTEST_REQUIRE_UNARY_FALSE
-
-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
// =================================================================================================
@@ -2344,7 +2569,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#else // DOCTEST_CONFIG_DISABLE
#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
- namespace { \
+ namespace /* NOLINT */ { \
template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
struct der : public base \
{ void f(); }; \
@@ -2358,51 +2583,48 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// for registering tests
#define DOCTEST_TEST_CASE(name) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for registering tests in classes
#define DOCTEST_TEST_CASE_CLASS(name) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for registering tests with a fixture
#define DOCTEST_TEST_CASE_FIXTURE(x, name) \
- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for converting types to strings without the <typeinfo> header and demangling
-#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
-#define DOCTEST_TYPE_TO_STRING_IMPL(...)
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "")
+#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "")
// for typed tests
#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \
template <typename type> \
- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \
template <typename type> \
- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
-#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
-
-#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "")
+#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "")
// for subcases
#define DOCTEST_SUBCASE(name)
// for a testsuite block
-#define DOCTEST_TEST_SUITE(name) namespace
+#define DOCTEST_TEST_SUITE(name) namespace // NOLINT
// for starting a testsuite block
-#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "")
// for ending a testsuite block
-#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
- static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
+ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature)
#define DOCTEST_REGISTER_REPORTER(name, priority, reporter)
#define DOCTEST_REGISTER_LISTENER(name, priority, reporter)
@@ -2416,80 +2638,253 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0))
#define DOCTEST_FAIL(...) (static_cast<void>(0))
-#define DOCTEST_WARN(...) (static_cast<void>(0))
-#define DOCTEST_CHECK(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_FALSE(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_EQ(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_EQ(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_EQ(...) (static_cast<void>(0))
-#define DOCTEST_WARN_NE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_GT(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_GT(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_GT(...) (static_cast<void>(0))
-#define DOCTEST_WARN_LT(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_LT(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_LT(...) (static_cast<void>(0))
-#define DOCTEST_WARN_GE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_GE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_GE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_LE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_LE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_LE(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_WARN_UNARY_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_UNARY_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_UNARY_FALSE(...) (static_cast<void>(0))
+#if defined(DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED) \
+ && defined(DOCTEST_CONFIG_ASSERTS_RETURN_VALUES)
+
+#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }()
+
+#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+
+namespace doctest {
+namespace detail {
+#define DOCTEST_RELATIONAL_OP(name, op) \
+ template <typename L, typename R> \
+ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; }
+
+ DOCTEST_RELATIONAL_OP(eq, ==)
+ DOCTEST_RELATIONAL_OP(ne, !=)
+ DOCTEST_RELATIONAL_OP(lt, <)
+ DOCTEST_RELATIONAL_OP(gt, >)
+ DOCTEST_RELATIONAL_OP(le, <=)
+ DOCTEST_RELATIONAL_OP(ge, >=)
+} // namespace detail
+} // namespace doctest
+
+#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_WARN_THROWS_WITH(expr, with, ...) [] { static_assert(false, "Exception translation is not available when doctest is disabled."); return false; }()
+#define DOCTEST_CHECK_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+
+#define DOCTEST_WARN_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_CHECK_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_REQUIRE_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_WARN_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_WARN_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_CHECK_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_REQUIRE_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
+
+#define DOCTEST_WARN(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_LE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_LE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_WARN_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
#endif // DOCTEST_CONFIG_DISABLE
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#define DOCTEST_EXCEPTION_EMPTY_FUNC DOCTEST_FUNC_EMPTY
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#define DOCTEST_EXCEPTION_EMPTY_FUNC [] { static_assert(false, "Exceptions are disabled! " \
+ "Use DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS if you want to compile with exceptions disabled."); return false; }()
+
+#undef DOCTEST_REQUIRE
+#undef DOCTEST_REQUIRE_FALSE
+#undef DOCTEST_REQUIRE_MESSAGE
+#undef DOCTEST_REQUIRE_FALSE_MESSAGE
+#undef DOCTEST_REQUIRE_EQ
+#undef DOCTEST_REQUIRE_NE
+#undef DOCTEST_REQUIRE_GT
+#undef DOCTEST_REQUIRE_LT
+#undef DOCTEST_REQUIRE_GE
+#undef DOCTEST_REQUIRE_LE
+#undef DOCTEST_REQUIRE_UNARY
+#undef DOCTEST_REQUIRE_UNARY_FALSE
+
+#define DOCTEST_REQUIRE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_FALSE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_EQ DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_GT DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_LT DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_GE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_LE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_UNARY DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_UNARY_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#define DOCTEST_WARN_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
// clang-format off
// KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS
#define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ
@@ -2536,11 +2931,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// clang-format on
// == SHORT VERSIONS OF THE MACROS
-#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
+#ifndef DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
#define TEST_CASE(name) DOCTEST_TEST_CASE(name)
#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name)
#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name)
+#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__)
#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__)
#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
@@ -2673,39 +3069,19 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
-#if !defined(DOCTEST_CONFIG_DISABLE)
+#ifndef DOCTEST_CONFIG_DISABLE
// this is here to clear the 'current test suite' for the current translation unit - at the top
DOCTEST_TEST_SUITE_END();
-// add stringification for primitive/fundamental types
-namespace doctest { namespace detail {
- DOCTEST_TYPE_TO_STRING_IMPL(bool)
- DOCTEST_TYPE_TO_STRING_IMPL(float)
- DOCTEST_TYPE_TO_STRING_IMPL(double)
- DOCTEST_TYPE_TO_STRING_IMPL(long double)
- DOCTEST_TYPE_TO_STRING_IMPL(char)
- DOCTEST_TYPE_TO_STRING_IMPL(signed char)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned char)
-#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED)
- DOCTEST_TYPE_TO_STRING_IMPL(wchar_t)
-#endif // not MSVC or wchar_t support enabled
- DOCTEST_TYPE_TO_STRING_IMPL(short int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int)
- DOCTEST_TYPE_TO_STRING_IMPL(int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned int)
- DOCTEST_TYPE_TO_STRING_IMPL(long int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int)
- DOCTEST_TYPE_TO_STRING_IMPL(long long int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int)
-}} // namespace doctest::detail
-
#endif // DOCTEST_CONFIG_DISABLE
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
+
#endif // DOCTEST_LIBRARY_INCLUDED
#ifndef DOCTEST_SINGLE_HEADER
@@ -2725,13 +3101,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros")
DOCTEST_CLANG_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations")
@@ -2739,65 +3113,35 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default")
DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data
-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled
DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified
DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal
DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch
-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs
-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C
-DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff
-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning)
-// static analysis
-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor...
-DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
+DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed
DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
@@ -2805,7 +3149,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <ctime>
#include <cmath>
#include <climits>
-// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37
+// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37
#ifdef __BORLANDC__
#include <math.h>
#endif // __BORLANDC__
@@ -2817,20 +3161,33 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <utility>
#include <fstream>
#include <sstream>
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <iostream>
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <algorithm>
#include <iomanip>
#include <vector>
+#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
#include <atomic>
#include <mutex>
+#define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
+#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+#define DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_DECLARE_STATIC_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name)
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
#include <set>
#include <map>
+#include <unordered_set>
#include <exception>
#include <stdexcept>
#include <csignal>
#include <cfloat>
#include <cctype>
#include <cstdint>
+#include <string>
#ifdef DOCTEST_PLATFORM_MAC
#include <sys/types.h>
@@ -2843,9 +3200,11 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
// defines for a leaner windows.h
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
+#define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
+#define DOCTEST_UNDEF_NOMINMAX
#endif // NOMINMAX
// not sure what AfxWin.h is for - here I do what Catch does
@@ -2863,7 +3222,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#endif // DOCTEST_PLATFORM_WINDOWS
-// this is a fix for https://github.com/onqtam/doctest/issues/348
+// this is a fix for https://github.com/doctest/doctest/issues/348
// https://mail.gnome.org/archives/xml/2012-January/msg00000.html
#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
#define STDOUT_FILENO fileno(stdout)
@@ -2885,8 +3244,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#endif
#ifndef DOCTEST_THREAD_LOCAL
+#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_THREAD_LOCAL
+#else // DOCTEST_MSVC
#define DOCTEST_THREAD_LOCAL thread_local
-#endif
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_THREAD_LOCAL
#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
@@ -2906,12 +3269,40 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
#endif
+#ifndef DOCTEST_CDECL
+#define DOCTEST_CDECL __cdecl
+#endif
+
namespace doctest {
bool is_running_in_test = false;
namespace {
using namespace detail;
+
+ template <typename Ex>
+ DOCTEST_NORETURN void throw_exception(Ex const& e) {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ throw e;
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION
+ DOCTEST_CONFIG_HANDLE_EXCEPTION(e);
+#else // DOCTEST_CONFIG_HANDLE_EXCEPTION
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ std::cerr << "doctest will terminate because it needed to throw an exception.\n"
+ << "The message was: " << e.what() << '\n';
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION
+ std::terminate();
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ }
+
+#ifndef DOCTEST_INTERNAL_ERROR
+#define DOCTEST_INTERNAL_ERROR(msg) \
+ throw_exception(std::logic_error( \
+ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
+#endif // DOCTEST_INTERNAL_ERROR
+
// case insensitive strcmp
int stricmp(const char* a, const char* b) {
for(;; a++, b++) {
@@ -2921,20 +3312,6 @@ namespace {
}
}
- template <typename T>
- String fpToString(T value, int precision) {
- std::ostringstream oss;
- oss << std::setprecision(precision) << std::fixed << value;
- std::string d = oss.str();
- size_t i = d.find_last_not_of('0');
- if(i != std::string::npos && i != d.size() - 1) {
- if(d[i] == '.')
- i++;
- d = d.substr(0, i + 1);
- }
- return d.c_str();
- }
-
struct Endianness
{
enum Arch
@@ -2955,58 +3332,56 @@ namespace {
} // namespace
namespace detail {
- void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); }
-
- String rawMemoryToString(const void* object, unsigned size) {
- // Reverse order for little endian architectures
- int i = 0, end = static_cast<int>(size), inc = 1;
- if(Endianness::which() == Endianness::Little) {
- i = end - 1;
- end = inc = -1;
- }
-
- unsigned const char* bytes = static_cast<unsigned const char*>(object);
- std::ostringstream oss;
- oss << "0x" << std::setfill('0') << std::hex;
- for(; i != end; i += inc)
- oss << std::setw(2) << static_cast<unsigned>(bytes[i]);
- return oss.str().c_str();
- }
+ DOCTEST_THREAD_LOCAL class
+ {
+ std::vector<std::streampos> stack;
+ std::stringstream ss;
- DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp)
+ public:
+ std::ostream* push() {
+ stack.push_back(ss.tellp());
+ return &ss;
+ }
+
+ String pop() {
+ if (stack.empty())
+ DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!");
+
+ std::streampos pos = stack.back();
+ stack.pop_back();
+ unsigned sz = static_cast<unsigned>(ss.tellp() - pos);
+ ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out);
+ return String(ss, sz);
+ }
+ } g_oss;
- std::ostream* getTlsOss() {
- g_oss.clear(); // there shouldn't be anything worth clearing in the flags
- g_oss.str(""); // the slow way of resetting a string stream
- //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383
- return &g_oss;
+ std::ostream* tlssPush() {
+ return g_oss.push();
}
- String getTlsOssResult() {
- //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383
- return g_oss.str().c_str();
+ String tlssPop() {
+ return g_oss.pop();
}
#ifndef DOCTEST_CONFIG_DISABLE
namespace timer_large_integer
{
-
+
#if defined(DOCTEST_PLATFORM_WINDOWS)
- typedef ULONGLONG type;
+ using type = ULONGLONG;
#else // DOCTEST_PLATFORM_WINDOWS
- using namespace std;
- typedef uint64_t type;
+ using type = std::uint64_t;
#endif // DOCTEST_PLATFORM_WINDOWS
}
-typedef timer_large_integer::type ticks_t;
+using ticks_t = timer_large_integer::type;
#ifdef DOCTEST_CONFIG_GETCURRENTTICKS
ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); }
#elif defined(DOCTEST_PLATFORM_WINDOWS)
ticks_t getCurrentTicks() {
- static LARGE_INTEGER hz = {0}, hzo = {0};
+ static LARGE_INTEGER hz = { {0} }, hzo = { {0} };
if(!hz.QuadPart) {
QueryPerformanceFrequency(&hz);
QueryPerformanceCounter(&hzo);
@@ -3038,9 +3413,17 @@ typedef timer_large_integer::type ticks_t;
ticks_t m_ticks = 0;
};
-#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = T;
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = std::atomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
+
+#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
template <typename T>
- using AtomicOrMultiLaneAtomic = std::atomic<T>;
+ using MultiLaneAtomic = Atomic<T>;
#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
// Provides a multilane implementation of an atomic variable that supports add, sub, load,
// store. Instead of using a single atomic variable, this splits up into multiple ones,
@@ -3057,8 +3440,8 @@ typedef timer_large_integer::type ticks_t;
{
struct CacheLineAlignedAtomic
{
- std::atomic<T> atomic{};
- char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+ Atomic<T> atomic{};
+ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
};
CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
@@ -3088,7 +3471,7 @@ typedef timer_large_integer::type ticks_t;
return result;
}
- T operator=(T desired) DOCTEST_NOEXCEPT {
+ T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this]
store(desired);
return desired;
}
@@ -3103,7 +3486,7 @@ typedef timer_large_integer::type ticks_t;
private:
// Each thread has a different atomic that it operates on. If more than NumLanes threads
- // use this, some will use the same atomic. So performance will degrate a bit, but still
+ // use this, some will use the same atomic. So performance will degrade a bit, but still
// everything will work.
//
// The logic here is a bit tricky. The call should be as fast as possible, so that there
@@ -3114,24 +3497,21 @@ typedef timer_large_integer::type ticks_t;
// assigned in a round-robin fashion.
// 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
// little overhead.
- std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
- static std::atomic<size_t> laneCounter;
+ Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+ static Atomic<size_t> laneCounter;
DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
return m_atomics[tlsLaneIdx].atomic;
}
};
-
- template <typename T>
- using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
// this holds both parameters from the command line and runtime data for tests
struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
{
- AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
- AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+ MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+ MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
@@ -3144,11 +3524,12 @@ typedef timer_large_integer::type ticks_t;
std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
// stuff for subcases
- std::vector<SubcaseSignature> subcasesStack;
- std::set<decltype(subcasesStack)> subcasesPassed;
- int subcasesCurrentMaxLevel;
- bool should_reenter;
- std::atomic<bool> shouldLogCurrentException;
+ bool reachedLeaf;
+ std::vector<SubcaseSignature> subcaseStack;
+ std::vector<SubcaseSignature> nextSubcaseStack;
+ std::unordered_set<unsigned long long> fullyTraversedSubcases;
+ size_t currentSubcaseDepth;
+ Atomic<bool> shouldLogCurrentException;
void resetRunData() {
numTestCases = 0;
@@ -3198,7 +3579,8 @@ typedef timer_large_integer::type ticks_t;
(TestCaseFailureReason::FailedExactlyNumTimes & failure_flags);
// if any subcase has failed - the whole test case has failed
- if(failure_flags && !ok_to_fail)
+ testCaseSuccess = !(failure_flags && !ok_to_fail);
+ if(!testCaseSuccess)
numTestCasesFailed++;
}
};
@@ -3213,23 +3595,37 @@ typedef timer_large_integer::type ticks_t;
#endif // DOCTEST_CONFIG_DISABLE
} // namespace detail
-void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
-void String::setLast(unsigned in) { buf[last] = char(in); }
+char* String::allocate(size_type sz) {
+ if (sz <= last) {
+ buf[sz] = '\0';
+ setLast(last - sz);
+ return buf;
+ } else {
+ setOnHeap();
+ data.size = sz;
+ data.capacity = data.size + 1;
+ data.ptr = new char[data.capacity];
+ data.ptr[sz] = '\0';
+ return data.ptr;
+ }
+}
+
+void String::setOnHeap() noexcept { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
+void String::setLast(size_type in) noexcept { buf[last] = char(in); }
+void String::setSize(size_type sz) noexcept {
+ if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); }
+ else { data.ptr[sz] = '\0'; data.size = sz; }
+}
void String::copy(const String& other) {
- using namespace std;
if(other.isOnStack()) {
memcpy(buf, other.buf, len);
} else {
- setOnHeap();
- data.size = other.data.size;
- data.capacity = data.size + 1;
- data.ptr = new char[data.capacity];
- memcpy(data.ptr, other.data.ptr, data.size + 1);
+ memcpy(allocate(other.data.size), other.data.ptr, other.data.size);
}
}
-String::String() {
+String::String() noexcept {
buf[0] = '\0';
setLast();
}
@@ -3237,26 +3633,17 @@ String::String() {
String::~String() {
if(!isOnStack())
delete[] data.ptr;
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
-}
+} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
String::String(const char* in)
: String(in, strlen(in)) {}
-String::String(const char* in, unsigned in_size) {
- using namespace std;
- if(in_size <= last) {
- memcpy(buf, in, in_size);
- buf[in_size] = '\0';
- setLast(last - in_size);
- } else {
- setOnHeap();
- data.size = in_size;
- data.capacity = data.size + 1;
- data.ptr = new char[data.capacity];
- memcpy(data.ptr, in, in_size);
- data.ptr[in_size] = '\0';
- }
+String::String(const char* in, size_type in_size) {
+ memcpy(allocate(in_size), in, in_size);
+}
+
+String::String(std::istream& in, size_type in_size) {
+ in.read(allocate(in_size), in_size);
}
String::String(const String& other) { copy(other); }
@@ -3273,10 +3660,9 @@ String& String::operator=(const String& other) {
}
String& String::operator+=(const String& other) {
- const unsigned my_old_size = size();
- const unsigned other_size = other.size();
- const unsigned total_size = my_old_size + other_size;
- using namespace std;
+ const size_type my_old_size = size();
+ const size_type other_size = other.size();
+ const size_type total_size = my_old_size + other_size;
if(isOnStack()) {
if(total_size < len) {
// append to the current stack space
@@ -3323,18 +3709,13 @@ String& String::operator+=(const String& other) {
return *this;
}
-// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
-String String::operator+(const String& other) const { return String(*this) += other; }
-
-String::String(String&& other) {
- using namespace std;
+String::String(String&& other) noexcept {
memcpy(buf, other.buf, len);
other.buf[0] = '\0';
other.setLast();
}
-String& String::operator=(String&& other) {
- using namespace std;
+String& String::operator=(String&& other) noexcept {
if(this != &other) {
if(!isOnStack())
delete[] data.ptr;
@@ -3345,30 +3726,60 @@ String& String::operator=(String&& other) {
return *this;
}
-char String::operator[](unsigned i) const {
- return const_cast<String*>(this)->operator[](i); // NOLINT
+char String::operator[](size_type i) const {
+ return const_cast<String*>(this)->operator[](i);
}
-char& String::operator[](unsigned i) {
+char& String::operator[](size_type i) {
if(isOnStack())
return reinterpret_cast<char*>(buf)[i];
return data.ptr[i];
}
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized")
-unsigned String::size() const {
+String::size_type String::size() const {
if(isOnStack())
- return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32
+ return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32
return data.size;
}
DOCTEST_GCC_SUPPRESS_WARNING_POP
-unsigned String::capacity() const {
+String::size_type String::capacity() const {
if(isOnStack())
return len;
return data.capacity;
}
+String String::substr(size_type pos, size_type cnt) && {
+ cnt = std::min(cnt, size() - 1 - pos);
+ char* cptr = c_str();
+ memmove(cptr, cptr + pos, cnt);
+ setSize(cnt);
+ return std::move(*this);
+}
+
+String String::substr(size_type pos, size_type cnt) const & {
+ cnt = std::min(cnt, size() - 1 - pos);
+ return String{ c_str() + pos, cnt };
+}
+
+String::size_type String::find(char ch, size_type pos) const {
+ const char* begin = c_str();
+ const char* end = begin + size();
+ const char* it = begin + pos;
+ for (; it < end && *it != ch; it++);
+ if (it < end) { return static_cast<size_type>(it - begin); }
+ else { return npos; }
+}
+
+String::size_type String::rfind(char ch, size_type pos) const {
+ const char* begin = c_str();
+ const char* it = begin + std::min(pos, size() - 1);
+ for (; it >= begin && *it != ch; it--);
+ if (it >= begin) { return static_cast<size_type>(it - begin); }
+ else { return npos; }
+}
+
int String::compare(const char* other, bool no_case) const {
if(no_case)
return doctest::stricmp(c_str(), other);
@@ -3379,17 +3790,32 @@ int String::compare(const String& other, bool no_case) const {
return compare(other.c_str(), no_case);
}
-// clang-format off
+String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; }
+
bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; }
bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; }
-// clang-format on
std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); }
+Contains::Contains(const String& str) : string(str) { }
+
+bool Contains::checkWith(const String& other) const {
+ return strstr(other.c_str(), string.c_str()) != nullptr;
+}
+
+String toString(const Contains& in) {
+ return "Contains( " + in.string + " )";
+}
+
+bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); }
+bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); }
+bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); }
+bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); }
+
namespace {
void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;)
} // namespace
@@ -3403,64 +3829,42 @@ namespace Color {
// clang-format off
const char* assertString(assertType::Enum at) {
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled
- switch(at) { //!OCLINT missing default in switch statements
- case assertType::DT_WARN : return "WARN";
- case assertType::DT_CHECK : return "CHECK";
- case assertType::DT_REQUIRE : return "REQUIRE";
-
- case assertType::DT_WARN_FALSE : return "WARN_FALSE";
- case assertType::DT_CHECK_FALSE : return "CHECK_FALSE";
- case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE";
-
- case assertType::DT_WARN_THROWS : return "WARN_THROWS";
- case assertType::DT_CHECK_THROWS : return "CHECK_THROWS";
- case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS";
-
- case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS";
- case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS";
- case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS";
-
- case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH";
- case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH";
- case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH";
-
- case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS";
- case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS";
- case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS";
-
- case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW";
- case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW";
- case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW";
-
- case assertType::DT_WARN_EQ : return "WARN_EQ";
- case assertType::DT_CHECK_EQ : return "CHECK_EQ";
- case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ";
- case assertType::DT_WARN_NE : return "WARN_NE";
- case assertType::DT_CHECK_NE : return "CHECK_NE";
- case assertType::DT_REQUIRE_NE : return "REQUIRE_NE";
- case assertType::DT_WARN_GT : return "WARN_GT";
- case assertType::DT_CHECK_GT : return "CHECK_GT";
- case assertType::DT_REQUIRE_GT : return "REQUIRE_GT";
- case assertType::DT_WARN_LT : return "WARN_LT";
- case assertType::DT_CHECK_LT : return "CHECK_LT";
- case assertType::DT_REQUIRE_LT : return "REQUIRE_LT";
- case assertType::DT_WARN_GE : return "WARN_GE";
- case assertType::DT_CHECK_GE : return "CHECK_GE";
- case assertType::DT_REQUIRE_GE : return "REQUIRE_GE";
- case assertType::DT_WARN_LE : return "WARN_LE";
- case assertType::DT_CHECK_LE : return "CHECK_LE";
- case assertType::DT_REQUIRE_LE : return "REQUIRE_LE";
-
- case assertType::DT_WARN_UNARY : return "WARN_UNARY";
- case assertType::DT_CHECK_UNARY : return "CHECK_UNARY";
- case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY";
- case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE";
- case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE";
- case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE";
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled
+ #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
+ #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type)
+ switch(at) {
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE);
+
+ default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!");
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP
- return "";
}
// clang-format on
@@ -3494,6 +3898,12 @@ const char* skipPathFromFilename(const char* file) {
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+bool SubcaseSignature::operator==(const SubcaseSignature& other) const {
+ return m_line == other.m_line
+ && std::strcmp(m_file, other.m_file) == 0
+ && m_name == other.m_name;
+}
+
bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
if(m_line != other.m_line)
return m_line < other.m_line;
@@ -3502,45 +3912,53 @@ bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
return m_name.compare(other.m_name) < 0;
}
-IContextScope::IContextScope() = default;
-IContextScope::~IContextScope() = default;
+DOCTEST_DEFINE_INTERFACE(IContextScope)
-#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-String toString(char* in) { return toString(static_cast<const char*>(in)); }
-// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
-String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-String toString(bool in) { return in ? "true" : "false"; }
-String toString(float in) { return fpToString(in, 5) + "f"; }
-String toString(double in) { return fpToString(in, 10); }
-String toString(double long in) { return fpToString(in, 15); }
-
-#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \
- String toString(type in) { \
- char buf[64]; \
- std::sprintf(buf, fmt, in); \
- return buf; \
+namespace detail {
+ void filldata<const void*>::fill(std::ostream* stream, const void* in) {
+ if (in) { *stream << in; }
+ else { *stream << "nullptr"; }
}
-DOCTEST_TO_STRING_OVERLOAD(char, "%d")
-DOCTEST_TO_STRING_OVERLOAD(char signed, "%d")
-DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int short, "%d")
-DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int, "%d")
-DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int long, "%ld")
-DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu")
-DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld")
-DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu")
+ template <typename T>
+ String toStreamLit(T t) {
+ std::ostream* os = tlssPush();
+ os->operator<<(t);
+ return tlssPop();
+ }
+}
-String toString(std::nullptr_t) { return "NULL"; }
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
String toString(const std::string& in) { return in.c_str(); }
#endif // VS 2019
+String toString(String in) { return in; }
+
+String toString(std::nullptr_t) { return "nullptr"; }
+
+String toString(bool in) { return in ? "true" : "false"; }
+
+String toString(float in) { return toStreamLit(in); }
+String toString(double in) { return toStreamLit(in); }
+String toString(double long in) { return toStreamLit(in); }
+
+String toString(char in) { return toStreamLit(static_cast<signed>(in)); }
+String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); }
+String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(in)); }
+String toString(short in) { return toStreamLit(in); }
+String toString(short unsigned in) { return toStreamLit(in); }
+String toString(signed in) { return toStreamLit(in); }
+String toString(unsigned in) { return toStreamLit(in); }
+String toString(long in) { return toStreamLit(in); }
+String toString(long unsigned in) { return toStreamLit(in); }
+String toString(long long in) { return toStreamLit(in); }
+String toString(long long unsigned in) { return toStreamLit(in); }
+
Approx::Approx(double value)
: m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100)
, m_scale(1.0)
@@ -3580,11 +3998,25 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs
bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
String toString(const Approx& in) {
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
- return String("Approx( ") + doctest::toString(in.m_value) + " )";
+ return "Approx( " + doctest::toString(in.m_value) + " )";
}
const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); }
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
+template <typename F>
+IsNaN<F>::operator bool() const {
+ return std::isnan(value) ^ flipped;
+}
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
+template <typename F>
+String toString(IsNaN<F> in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; }
+String toString(IsNaN<float> in) { return toString<float>(in); }
+String toString(IsNaN<double> in) { return toString<double>(in); }
+String toString(IsNaN<double long> in) { return toString<double long>(in); }
+
} // namespace doctest
#ifdef DOCTEST_CONFIG_DISABLE
@@ -3594,15 +4026,15 @@ Context::~Context() = default;
void Context::applyCommandLine(int, const char* const*) {}
void Context::addFilter(const char*, const char*) {}
void Context::clearFilters() {}
+void Context::setOption(const char*, bool) {}
void Context::setOption(const char*, int) {}
void Context::setOption(const char*, const char*) {}
bool Context::shouldExit() { return false; }
void Context::setAsDefaultForAssertsOutOfTestCases() {}
void Context::setAssertHandler(detail::assert_handler) {}
+void Context::setCout(std::ostream*) {}
int Context::run() { return 0; }
-IReporter::~IReporter() = default;
-
int IReporter::get_num_active_contexts() { return 0; }
const IContextScope* const* IReporter::get_active_contexts() { return nullptr; }
int IReporter::get_num_stringified_contexts() { return 0; }
@@ -3635,7 +4067,7 @@ namespace doctest {
namespace {
// the int (priority) is part of the key for automatic sorting - sadly one can register a
// reporter with a duplicate name and a different priority but hopefully that won't happen often :|
- typedef std::map<std::pair<int, String>, reporterCreatorFunc> reporterMap;
+ using reporterMap = std::map<std::pair<int, String>, reporterCreatorFunc>;
reporterMap& getReporters() {
static reporterMap data;
@@ -3667,8 +4099,8 @@ namespace detail {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
DOCTEST_NORETURN void throwException() {
g_cs->shouldLogCurrentException = false;
- throw TestFailureException();
- } // NOLINT(cert-err60-cpp)
+ throw TestFailureException(); // NOLINT(hicpp-exception-baseclass)
+ }
#else // DOCTEST_CONFIG_NO_EXCEPTIONS
void throwException() {}
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -3714,91 +4146,134 @@ namespace {
return !*wild;
}
- //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
- //unsigned hashStr(unsigned const char* str) {
- // unsigned long hash = 5381;
- // char c;
- // while((c = *str++))
- // hash = ((hash << 5) + hash) + c; // hash * 33 + c
- // return hash;
- //}
-
// checks if the name matches any of the filters (and can be configured what to do when empty)
bool matchesAny(const char* name, const std::vector<String>& filters, bool matchEmpty,
- bool caseSensitive) {
- if(filters.empty() && matchEmpty)
+ bool caseSensitive) {
+ if (filters.empty() && matchEmpty)
return true;
- for(auto& curr : filters)
- if(wildcmp(name, curr.c_str(), caseSensitive))
+ for (auto& curr : filters)
+ if (wildcmp(name, curr.c_str(), caseSensitive))
return true;
return false;
}
-} // namespace
-namespace detail {
- Subcase::Subcase(const String& name, const char* file, int line)
- : m_signature({name, file, line}) {
- auto* s = g_cs;
+ DOCTEST_NO_SANITIZE_INTEGER
+ unsigned long long hash(unsigned long long a, unsigned long long b) {
+ return (a << 5) + b;
+ }
- // check subcase filters
- if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) {
- if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive))
- return;
- if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive))
- return;
- }
-
- // if a Subcase on the same level has already been entered
- if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) {
- s->should_reenter = true;
- return;
- }
+ // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
+ DOCTEST_NO_SANITIZE_INTEGER
+ unsigned long long hash(const char* str) {
+ unsigned long long hash = 5381;
+ char c;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) + c; // hash * 33 + c
+ return hash;
+ }
- // push the current signature to the stack so we can check if the
- // current stack + the current new subcase have been traversed
- s->subcasesStack.push_back(m_signature);
- if(s->subcasesPassed.count(s->subcasesStack) != 0) {
- // pop - revert to previous stack since we've already passed this
- s->subcasesStack.pop_back();
- return;
+ unsigned long long hash(const SubcaseSignature& sig) {
+ return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line);
+ }
+
+ unsigned long long hash(const std::vector<SubcaseSignature>& sigs, size_t count) {
+ unsigned long long running = 0;
+ auto end = sigs.begin() + count;
+ for (auto it = sigs.begin(); it != end; it++) {
+ running = hash(running, hash(*it));
}
+ return running;
+ }
- s->subcasesCurrentMaxLevel = s->subcasesStack.size();
- m_entered = true;
+ unsigned long long hash(const std::vector<SubcaseSignature>& sigs) {
+ unsigned long long running = 0;
+ for (const SubcaseSignature& sig : sigs) {
+ running = hash(running, hash(sig));
+ }
+ return running;
+ }
+} // namespace
+namespace detail {
+ bool Subcase::checkFilters() {
+ if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) {
+ if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive))
+ return true;
+ if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive))
+ return true;
+ }
+ return false;
+ }
- DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ Subcase::Subcase(const String& name, const char* file, int line)
+ : m_signature({name, file, line}) {
+ if (!g_cs->reachedLeaf) {
+ if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size()
+ || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) {
+ // Going down.
+ if (checkFilters()) { return; }
+
+ g_cs->subcaseStack.push_back(m_signature);
+ g_cs->currentSubcaseDepth++;
+ m_entered = true;
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ }
+ } else {
+ if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) {
+ // This subcase is reentered via control flow.
+ g_cs->currentSubcaseDepth++;
+ m_entered = true;
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth
+ && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature)))
+ == g_cs->fullyTraversedSubcases.end()) {
+ if (checkFilters()) { return; }
+ // This subcase is part of the one to be executed next.
+ g_cs->nextSubcaseStack.clear();
+ g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(),
+ g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth);
+ g_cs->nextSubcaseStack.push_back(m_signature);
+ }
+ }
}
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
- DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
Subcase::~Subcase() {
- if(m_entered) {
- // only mark the subcase stack as passed if no subcases have been skipped
- if(g_cs->should_reenter == false)
- g_cs->subcasesPassed.insert(g_cs->subcasesStack);
- g_cs->subcasesStack.pop_back();
+ if (m_entered) {
+ g_cs->currentSubcaseDepth--;
+
+ if (!g_cs->reachedLeaf) {
+ // Leaf.
+ g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
+ g_cs->nextSubcaseStack.clear();
+ g_cs->reachedLeaf = true;
+ } else if (g_cs->nextSubcaseStack.empty()) {
+ // All children are finished.
+ g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
+ }
#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0
#else
if(std::uncaught_exception()
#endif
- && g_cs->shouldLogCurrentException) {
+ && g_cs->shouldLogCurrentException) {
DOCTEST_ITERATE_THROUGH_REPORTERS(
test_case_exception, {"exception thrown in subcase - will translate later "
- "when the whole test case has been exited (cannot "
- "translate while there is an active exception)",
- false});
+ "when the whole test case has been exited (cannot "
+ "translate while there is an active exception)",
+ false});
g_cs->shouldLogCurrentException = false;
}
+
DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
}
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
- DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
Subcase::operator bool() const { return m_entered; }
@@ -3812,20 +4287,11 @@ namespace detail {
TestSuite& TestSuite::operator*(const char* in) {
m_test_suite = in;
- // clear state
- m_description = nullptr;
- m_skip = false;
- m_no_breaks = false;
- m_no_output = false;
- m_may_fail = false;
- m_should_fail = false;
- m_expected_failures = 0;
- m_timeout = 0;
return *this;
}
TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
- const char* type, int template_id) {
+ const String& type, int template_id) {
m_file = file;
m_line = line;
m_name = nullptr; // will be later overridden in operator*
@@ -3850,10 +4316,8 @@ namespace detail {
}
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
- DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice
TestCase& TestCase::operator=(const TestCase& other) {
- static_cast<TestCaseData&>(*this) = static_cast<const TestCaseData&>(other);
-
+ TestCaseData::operator=(other);
m_test = other.m_test;
m_type = other.m_type;
m_template_id = other.m_template_id;
@@ -3869,7 +4333,7 @@ namespace detail {
m_name = in;
// make a new name with an appended type for templated test case
if(m_template_id != -1) {
- m_full_name = String(m_name) + m_type;
+ m_full_name = String(m_name) + "<" + m_type + ">";
// redirect the name to point to the newly constructed full name
m_name = m_full_name.c_str();
}
@@ -3925,29 +4389,6 @@ namespace {
return suiteOrderComparator(lhs, rhs);
}
-#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
- HANDLE g_stdoutHandle;
- WORD g_origFgAttrs;
- WORD g_origBgAttrs;
- bool g_attrsInitted = false;
-
- int colors_init() {
- if(!g_attrsInitted) {
- g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
- g_attrsInitted = true;
- CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
- GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo);
- g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
- BACKGROUND_BLUE | BACKGROUND_INTENSITY);
- g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
- FOREGROUND_BLUE | FOREGROUND_INTENSITY);
- }
- return 0;
- }
-
- int dumy_init_console_colors = colors_init();
-#endif // DOCTEST_CONFIG_COLORS_WINDOWS
-
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
void color_to_stream(std::ostream& s, Color::Enum code) {
static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
@@ -3981,10 +4422,26 @@ namespace {
#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
if(g_no_colors ||
- (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false))
+ (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
return;
-#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs)
+ static struct ConsoleHelper {
+ HANDLE stdoutHandle;
+ WORD origFgAttrs;
+ WORD origBgAttrs;
+
+ ConsoleHelper() {
+ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
+ origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
+ BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+ origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ }
+ } ch;
+
+#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs)
// clang-format off
switch (code) {
@@ -4001,7 +4458,7 @@ namespace {
case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
case Color::None:
case Color::Bright: // invalid
- default: DOCTEST_SET_ATTR(g_origFgAttrs);
+ default: DOCTEST_SET_ATTR(ch.origFgAttrs);
}
// clang-format on
#endif // DOCTEST_CONFIG_COLORS_WINDOWS
@@ -4118,35 +4575,22 @@ namespace detail {
getExceptionTranslators().push_back(et);
}
-#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- void toStream(std::ostream* s, char* in) { *s << in; }
- void toStream(std::ostream* s, const char* in) { *s << in; }
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; }
- void toStream(std::ostream* s, float in) { *s << in; }
- void toStream(std::ostream* s, double in) { *s << in; }
- void toStream(std::ostream* s, double long in) { *s << in; }
-
- void toStream(std::ostream* s, char in) { *s << in; }
- void toStream(std::ostream* s, char signed in) { *s << in; }
- void toStream(std::ostream* s, char unsigned in) { *s << in; }
- void toStream(std::ostream* s, int short in) { *s << in; }
- void toStream(std::ostream* s, int short unsigned in) { *s << in; }
- void toStream(std::ostream* s, int in) { *s << in; }
- void toStream(std::ostream* s, int unsigned in) { *s << in; }
- void toStream(std::ostream* s, int long in) { *s << in; }
- void toStream(std::ostream* s, int long unsigned in) { *s << in; }
- void toStream(std::ostream* s, int long long in) { *s << in; }
- void toStream(std::ostream* s, int long long unsigned in) { *s << in; }
-
DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO()
ContextScopeBase::ContextScopeBase() {
g_infoContexts.push_back(this);
}
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
- DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept {
+ if (other.need_to_destroy) {
+ other.destroy();
+ }
+ other.need_to_destroy = false;
+ g_infoContexts.push_back(this);
+ }
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
// destroy cannot be inlined into the destructor because that would mean calling stringify after
@@ -4165,8 +4609,8 @@ namespace detail {
g_infoContexts.pop_back();
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
- DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
} // namespace detail
namespace {
@@ -4207,10 +4651,10 @@ namespace {
static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
// Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
// console just once no matter how many threads have crashed.
- static std::mutex mutex;
+ DOCTEST_DECLARE_STATIC_MUTEX(mutex)
static bool execute = true;
{
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
if(execute) {
bool reported = false;
for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
@@ -4313,7 +4757,7 @@ namespace {
static unsigned int prev_abort_behavior;
static int prev_report_mode;
static _HFILE prev_report_file;
- static void (*prev_sigabrt_handler)(int);
+ static void (DOCTEST_CDECL *prev_sigabrt_handler)(int);
static std::terminate_handler original_terminate_handler;
static bool isSet;
static ULONG guaranteeSize;
@@ -4325,7 +4769,7 @@ namespace {
unsigned int FatalConditionHandler::prev_abort_behavior;
int FatalConditionHandler::prev_report_mode;
_HFILE FatalConditionHandler::prev_report_file;
- void (*FatalConditionHandler::prev_sigabrt_handler)(int);
+ void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int);
std::terminate_handler FatalConditionHandler::original_terminate_handler;
bool FatalConditionHandler::isSet = false;
ULONG FatalConditionHandler::guaranteeSize = 0;
@@ -4383,7 +4827,7 @@ namespace {
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = {};
- sa.sa_handler = handleSignal; // NOLINT
+ sa.sa_handler = handleSignal;
sa.sa_flags = SA_ONSTACK;
for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
@@ -4422,7 +4866,7 @@ namespace {
#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text)
#else
// TODO: integration with XCode and other IDEs
-#define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros)
+#define DOCTEST_OUTPUT_DEBUG_STRING(text)
#endif // Platform
void addAssert(assertType::Enum at) {
@@ -4441,8 +4885,8 @@ namespace {
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true});
- while(g_cs->subcasesStack.size()) {
- g_cs->subcasesStack.pop_back();
+ while (g_cs->subcaseStack.size()) {
+ g_cs->subcaseStack.pop_back();
DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
}
@@ -4454,25 +4898,26 @@ namespace {
}
#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
} // namespace
-namespace detail {
- ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
- const char* exception_type, const char* exception_string) {
- m_test_case = g_cs->currentTest;
- m_at = at;
- m_file = file;
- m_line = line;
- m_expr = expr;
- m_failed = true;
- m_threw = false;
- m_threw_as = false;
- m_exception_type = exception_type;
- m_exception_string = exception_string;
+AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string)
+ : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr),
+ m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type),
+ m_exception_string(exception_string) {
#if DOCTEST_MSVC
- if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
- ++m_expr;
+ if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
+ ++m_expr;
#endif // MSVC
- }
+}
+
+namespace detail {
+ ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const String& exception_string)
+ : AssertData(at, file, line, expr, exception_type, exception_string) { }
+
+ ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string)
+ : AssertData(at, file, line, expr, exception_type, exception_string) { }
void ResultBuilder::setResult(const Result& res) {
m_decomp = res.m_decomp;
@@ -4488,17 +4933,17 @@ namespace detail {
if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
m_failed = !m_threw;
} else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT
- m_failed = !m_threw_as || (m_exception != m_exception_string);
+ m_failed = !m_threw_as || !m_exception_string.check(m_exception);
} else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
m_failed = !m_threw_as;
} else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
- m_failed = m_exception != m_exception_string;
+ m_failed = !m_exception_string.check(m_exception);
} else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
m_failed = m_threw;
}
if(m_exception.size())
- m_exception = String("\"") + m_exception + "\"";
+ m_exception = "\"" + m_exception + "\"";
if(is_running_in_test) {
addAssert(m_at);
@@ -4526,8 +4971,8 @@ namespace detail {
std::abort();
}
- void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
- Result result) {
+ bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
+ const Result& result) {
bool failed = !result.m_passed;
// ###################################################################################
@@ -4536,21 +4981,29 @@ namespace detail {
// ###################################################################################
DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp);
DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ return !failed;
}
MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
- m_stream = getTlsOss();
+ m_stream = tlssPush();
m_file = file;
m_line = line;
m_severity = severity;
}
- IExceptionTranslator::IExceptionTranslator() = default;
- IExceptionTranslator::~IExceptionTranslator() = default;
+ MessageBuilder::~MessageBuilder() {
+ if (!logged)
+ tlssPop();
+ }
+
+ DOCTEST_DEFINE_INTERFACE(IExceptionTranslator)
bool MessageBuilder::log() {
- m_string = getTlsOssResult();
+ if (!logged) {
+ m_string = tlssPop();
+ logged = true;
+ }
+
DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
const bool isWarn = m_severity & assertType::is_warn;
@@ -4569,29 +5022,10 @@ namespace detail {
if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional
throwException();
}
-
- MessageBuilder::~MessageBuilder() = default;
} // namespace detail
namespace {
using namespace detail;
- template <typename Ex>
- DOCTEST_NORETURN void throw_exception(Ex const& e) {
-#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
- throw e;
-#else // DOCTEST_CONFIG_NO_EXCEPTIONS
- std::cerr << "doctest will terminate because it needed to throw an exception.\n"
- << "The message was: " << e.what() << '\n';
- std::terminate();
-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
- }
-
-#ifndef DOCTEST_INTERNAL_ERROR
-#define DOCTEST_INTERNAL_ERROR(msg) \
- throw_exception(std::logic_error( \
- __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
-#endif // DOCTEST_INTERNAL_ERROR
-
// clang-format off
// =================================================================================================
@@ -4638,7 +5072,11 @@ namespace {
mutable XmlWriter* m_writer = nullptr;
};
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
XmlWriter( std::ostream& os = std::cout );
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ XmlWriter( std::ostream& os );
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
~XmlWriter();
XmlWriter( XmlWriter const& ) = delete;
@@ -4673,10 +5111,10 @@ namespace {
void ensureTagClosed();
- private:
-
void writeDeclaration();
+ private:
+
void newlineIfNecessary();
bool m_tagIsOpen = false;
@@ -4865,7 +5303,7 @@ namespace {
XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
{
- writeDeclaration();
+ // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627
}
XmlWriter::~XmlWriter() {
@@ -4976,8 +5414,8 @@ namespace {
struct XmlReporter : public IReporter
{
- XmlWriter xml;
- std::mutex mutex;
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
@@ -5054,7 +5492,8 @@ namespace {
xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name)
.writeAttribute("testsuite", in.data[i]->m_test_suite)
.writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str()))
- .writeAttribute("line", line(in.data[i]->m_line));
+ .writeAttribute("line", line(in.data[i]->m_line))
+ .writeAttribute("skipped", in.data[i]->m_skip);
}
xml.scopedElement("OverallResultsTestCases")
.writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
@@ -5070,6 +5509,8 @@ namespace {
}
void test_run_start() override {
+ xml.writeDeclaration();
+
// remove .exe extension - mainly to have the same output on UNIX and Windows
std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
#ifdef DOCTEST_PLATFORM_WINDOWS
@@ -5117,14 +5558,15 @@ namespace {
test_case_start_impl(in);
xml.ensureTagClosed();
}
-
+
void test_case_reenter(const TestCaseData&) override {}
void test_case_end(const CurrentTestCaseStats& st) override {
xml.startElement("OverallResultsAsserts")
.writeAttribute("successes",
st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest)
- .writeAttribute("failures", st.numAssertsFailedCurrentTest);
+ .writeAttribute("failures", st.numAssertsFailedCurrentTest)
+ .writeAttribute("test_case_success", st.testCaseSuccess);
if(opt.duration)
xml.writeAttribute("duration", st.seconds);
if(tc->m_expected_failures)
@@ -5135,7 +5577,7 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.scopedElement("Exception")
.writeAttribute("crash", e.is_crash)
@@ -5143,8 +5585,6 @@ namespace {
}
void subcase_start(const SubcaseSignature& in) override {
- std::lock_guard<std::mutex> lock(mutex);
-
xml.startElement("SubCase")
.writeAttribute("name", in.m_name)
.writeAttribute("filename", skipPathFromFilename(in.m_file))
@@ -5158,7 +5598,7 @@ namespace {
if(!rb.m_failed && !opt.success)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.startElement("Expression")
.writeAttribute("success", !rb.m_failed)
@@ -5174,7 +5614,7 @@ namespace {
if(rb.m_at & assertType::is_throws_as)
xml.scopedElement("ExpectedException").writeText(rb.m_exception_type);
if(rb.m_at & assertType::is_throws_with)
- xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string);
+ xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str());
if((rb.m_at & assertType::is_normal) && !rb.m_threw)
xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str());
@@ -5184,7 +5624,7 @@ namespace {
}
void log_message(const MessageData& mb) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.startElement("Message")
.writeAttribute("type", failureString(mb.m_severity))
@@ -5220,7 +5660,8 @@ namespace {
} else if((rb.m_at & assertType::is_throws_as) &&
(rb.m_at & assertType::is_throws_with)) { //!OCLINT
s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None;
+ << rb.m_exception_string.c_str()
+ << "\", " << rb.m_exception_type << " ) " << Color::None;
if(rb.m_threw) {
if(!rb.m_failed) {
s << "threw as expected!\n";
@@ -5241,7 +5682,8 @@ namespace {
} else if(rb.m_at &
assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\" ) " << Color::None
+ << rb.m_exception_string.c_str()
+ << "\" ) " << Color::None
<< (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
"threw a DIFFERENT exception: ") :
"did NOT throw at all!")
@@ -5266,8 +5708,8 @@ namespace {
// - more attributes in tags
struct JUnitReporter : public IReporter
{
- XmlWriter xml;
- std::mutex mutex;
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
Timer timer;
std::vector<String> deepestSubcaseStackNames;
@@ -5363,9 +5805,13 @@ namespace {
// WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
// =========================================================================================
- void report_query(const QueryData&) override {}
+ void report_query(const QueryData&) override {
+ xml.writeDeclaration();
+ }
- void test_run_start() override {}
+ void test_run_start() override {
+ xml.writeDeclaration();
+ }
void test_run_end(const TestRunStats& p) override {
// remove .exe extension - mainly to have the same output on UNIX and Windows
@@ -5435,12 +5881,11 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
testCaseData.addError("exception", e.error_string.c_str());
}
void subcase_start(const SubcaseSignature& in) override {
- std::lock_guard<std::mutex> lock(mutex);
deepestSubcaseStackNames.push_back(in.m_name);
}
@@ -5450,7 +5895,7 @@ namespace {
if(!rb.m_failed) // report only failures & ignore the `success` option
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
std::ostringstream os;
os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
@@ -5461,7 +5906,22 @@ namespace {
testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
}
- void log_message(const MessageData&) override {}
+ void log_message(const MessageData& mb) override {
+ if(mb.m_severity & assertType::is_warn) // report only failures
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ std::ostringstream os;
+ os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(")
+ << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
+
+ os << mb.m_string.c_str() << "\n";
+ log_contexts(os);
+
+ testCaseData.addFailure(mb.m_string.c_str(),
+ mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str());
+ }
void test_case_skipped(const TestCaseData&) override {}
@@ -5501,7 +5961,7 @@ namespace {
bool hasLoggedCurrentTestStart;
std::vector<SubcaseSignature> subcasesStack;
size_t currentSubcaseLevel;
- std::mutex mutex;
+ DOCTEST_DECLARE_MUTEX(mutex)
// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
@@ -5606,9 +6066,11 @@ namespace {
}
void printIntro() {
- printVersion();
- s << Color::Cyan << "[doctest] " << Color::None
- << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
+ if(opt.no_intro == false) {
+ printVersion();
+ s << Color::Cyan << "[doctest] " << Color::None
+ << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
+ }
}
void printHelp() {
@@ -5693,12 +6155,18 @@ namespace {
<< Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "no console output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> "
@@ -5736,22 +6204,6 @@ namespace {
printReporters(getReporters(), "reporters");
}
- void list_query_results() {
- separator_to_stream();
- if(opt.count || opt.list_test_cases) {
- s << Color::Cyan << "[doctest] " << Color::None
- << "unskipped test cases passing the current filters: "
- << g_cs->numTestCasesPassingFilters << "\n";
- } else if(opt.list_test_suites) {
- s << Color::Cyan << "[doctest] " << Color::None
- << "unskipped test cases passing the current filters: "
- << g_cs->numTestCasesPassingFilters << "\n";
- s << Color::Cyan << "[doctest] " << Color::None
- << "test suites with unskipped test cases passing the current filters: "
- << g_cs->numTestSuitesPassingFilters << "\n";
- }
- }
-
// =========================================================================================
// WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
// =========================================================================================
@@ -5797,15 +6249,21 @@ namespace {
}
}
- void test_run_start() override { printIntro(); }
+ void test_run_start() override {
+ if(!opt.minimal)
+ printIntro();
+ }
void test_run_end(const TestRunStats& p) override {
+ if(opt.minimal && p.numTestCasesFailed == 0)
+ return;
+
separator_to_stream();
s << std::dec;
- auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
- auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
- auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
+ auto totwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
+ auto passwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
+ auto failwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
<< p.numTestCasesPassingFilters << " | "
@@ -5837,7 +6295,7 @@ namespace {
subcasesStack.clear();
currentSubcaseLevel = 0;
}
-
+
void test_case_reenter(const TestCaseData&) override {
subcasesStack.clear();
}
@@ -5849,7 +6307,7 @@ namespace {
// log the preamble of the test case only if there is something
// else to print - something other than that an assert has failed
if(opt.duration ||
- (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure))
+ (st.failure_flags && st.failure_flags != static_cast<int>(TestCaseFailureReason::AssertFailure)))
logTestStart();
if(opt.duration)
@@ -5880,6 +6338,7 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
+ DOCTEST_LOCK_MUTEX(mutex)
if(tc->m_no_output)
return;
@@ -5904,14 +6363,12 @@ namespace {
}
void subcase_start(const SubcaseSignature& subc) override {
- std::lock_guard<std::mutex> lock(mutex);
subcasesStack.push_back(subc);
++currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
void subcase_end() override {
- std::lock_guard<std::mutex> lock(mutex);
--currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
@@ -5920,7 +6377,7 @@ namespace {
if((!rb.m_failed && !opt.success) || tc->m_no_output)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
logTestStart();
@@ -5936,7 +6393,7 @@ namespace {
if(tc->m_no_output)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
logTestStart();
@@ -6047,18 +6504,42 @@ namespace {
std::vector<String>& res) {
String filtersString;
if(parseOption(argc, argv, pattern, &filtersString)) {
- // tokenize with "," as a separator
- // cppcheck-suppress strtokCalled
- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
- auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string
- while(pch != nullptr) {
- if(strlen(pch))
- res.push_back(pch);
- // uses the strtok() internal state to go to the next token
- // cppcheck-suppress strtokCalled
- pch = std::strtok(nullptr, ",");
+ // tokenize with "," as a separator, unless escaped with backslash
+ std::ostringstream s;
+ auto flush = [&s, &res]() {
+ auto string = s.str();
+ if(string.size() > 0) {
+ res.push_back(string.c_str());
+ }
+ s.str("");
+ };
+
+ bool seenBackslash = false;
+ const char* current = filtersString.c_str();
+ const char* end = current + strlen(current);
+ while(current != end) {
+ char character = *current++;
+ if(seenBackslash) {
+ seenBackslash = false;
+ if(character == ',' || character == '\\') {
+ s.put(character);
+ continue;
+ }
+ s.put('\\');
+ }
+ if(character == '\\') {
+ seenBackslash = true;
+ } else if(character == ',') {
+ flush();
+ } else {
+ s.put(character);
+ }
+ }
+
+ if(seenBackslash) {
+ s.put('\\');
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ flush();
return true;
}
return false;
@@ -6077,30 +6558,30 @@ namespace {
if(!parseOption(argc, argv, pattern, &parsedValue))
return false;
- if(type == 0) {
+ if(type) {
+ // integer
+ // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse...
+ int theInt = std::atoi(parsedValue.c_str());
+ if (theInt != 0) {
+ res = theInt; //!OCLINT parameter reassignment
+ return true;
+ }
+ } else {
// boolean
- const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1
- const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1
+ const char positive[][5] = { "1", "true", "on", "yes" }; // 5 - strlen("true") + 1
+ const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1
// if the value matches any of the positive/negative possibilities
- for(unsigned i = 0; i < 4; i++) {
- if(parsedValue.compare(positive[i], true) == 0) {
+ for (unsigned i = 0; i < 4; i++) {
+ if (parsedValue.compare(positive[i], true) == 0) {
res = 1; //!OCLINT parameter reassignment
return true;
}
- if(parsedValue.compare(negative[i], true) == 0) {
+ if (parsedValue.compare(negative[i], true) == 0) {
res = 0; //!OCLINT parameter reassignment
return true;
}
}
- } else {
- // integer
- // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse...
- int theInt = std::atoi(parsedValue.c_str()); // NOLINT
- if(theInt != 0) {
- res = theInt; //!OCLINT parameter reassignment
- return true;
- }
}
return false;
}
@@ -6191,9 +6672,12 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false);
@@ -6257,10 +6741,14 @@ void Context::clearFilters() {
curr.clear();
}
-// allows the user to override procedurally the int/bool options from the command line
+// allows the user to override procedurally the bool options from the command line
+void Context::setOption(const char* option, bool value) {
+ setOption(option, value ? "true" : "false");
+}
+
+// allows the user to override procedurally the int options from the command line
void Context::setOption(const char* option, int value) {
setOption(option, toString(value).c_str());
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
// allows the user to override procedurally the string options from the command line
@@ -6277,6 +6765,31 @@ void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; }
void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; }
+void Context::setCout(std::ostream* out) { p->cout = out; }
+
+static class DiscardOStream : public std::ostream
+{
+private:
+ class : public std::streambuf
+ {
+ private:
+ // allowing some buffering decreases the amount of calls to overflow
+ char buf[1024];
+
+ protected:
+ std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; }
+
+ int_type overflow(int_type ch) override {
+ setp(std::begin(buf), std::end(buf));
+ return traits_type::not_eof(ch);
+ }
+ } discardBuf;
+
+public:
+ DiscardOStream()
+ : std::ostream(&discardBuf) {}
+} discardOut;
+
// the main function that does all the filtering and test running
int Context::run() {
using namespace detail;
@@ -6290,15 +6803,22 @@ int Context::run() {
g_no_colors = p->no_colors;
p->resetRunData();
- // stdout by default
- p->cout = &std::cout;
- p->cerr = &std::cerr;
-
- // or to a file if specified
std::fstream fstr;
- if(p->out.size()) {
- fstr.open(p->out.c_str(), std::fstream::out);
- p->cout = &fstr;
+ if(p->cout == nullptr) {
+ if(p->quiet) {
+ p->cout = &discardOut;
+ } else if(p->out.size()) {
+ // to a file if specified
+ fstr.open(p->out.c_str(), std::fstream::out);
+ p->cout = &fstr;
+ } else {
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ // stdout by default
+ p->cout = &std::cout;
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ return EXIT_FAILURE;
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ }
}
FatalConditionHandler::allocateAltStackMem();
@@ -6370,7 +6890,7 @@ int Context::run() {
// random_shuffle implementation
const auto first = &testArray[0];
for(size_t i = testArray.size() - 1; i > 0; --i) {
- int idxToSwap = std::rand() % (i + 1); // NOLINT
+ int idxToSwap = std::rand() % (i + 1);
const auto temp = first[i];
@@ -6457,19 +6977,20 @@ int Context::run() {
p->numAssertsFailedCurrentTest_atomic = 0;
p->numAssertsCurrentTest_atomic = 0;
- p->subcasesPassed.clear();
+ p->fullyTraversedSubcases.clear();
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
p->timer.start();
-
+
bool run_test = true;
do {
// reset some of the fields for subcases (except for the set of fully passed ones)
- p->should_reenter = false;
- p->subcasesCurrentMaxLevel = 0;
- p->subcasesStack.clear();
+ p->reachedLeaf = false;
+ // May not be empty if previous subcase exited via exception.
+ p->subcaseStack.clear();
+ p->currentSubcaseDepth = 0;
p->shouldLogCurrentException = true;
@@ -6502,10 +7023,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
run_test = false;
p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
}
-
- if(p->should_reenter && run_test)
+
+ if(!p->nextSubcaseStack.empty() && run_test)
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
- if(!p->should_reenter)
+ if(p->nextSubcaseStack.empty())
run_test = false;
} while(run_test);
@@ -6531,17 +7052,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata);
}
- // see these issues on the reasoning for this:
- // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903
- // - https://github.com/onqtam/doctest/issues/126
- auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE
- { std::cout << std::string(); };
- DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS();
-
return cleanup_and_return();
}
-IReporter::~IReporter() = default;
+DOCTEST_DEFINE_INTERFACE(IReporter)
int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); }
const IContextScope* const* IReporter::get_active_contexts() {
@@ -6576,5 +7090,17 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
+
#endif // DOCTEST_LIBRARY_IMPLEMENTATION
#endif // DOCTEST_CONFIG_IMPLEMENT
+
+#ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN
+#undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+
+#ifdef DOCTEST_UNDEF_NOMINMAX
+#undef NOMINMAX
+#undef DOCTEST_UNDEF_NOMINMAX
+#endif // DOCTEST_UNDEF_NOMINMAX
diff --git a/contrib/doctest/doctest/parts/doctest.cpp b/contrib/doctest/doctest/parts/doctest.cpp
index 5b7442086..5a2dd4599 100644
--- a/contrib/doctest/doctest/parts/doctest.cpp
+++ b/contrib/doctest/doctest/parts/doctest.cpp
@@ -11,13 +11,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros")
DOCTEST_CLANG_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations")
@@ -25,65 +23,35 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum")
DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default")
DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function")
DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data
-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled
DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified
DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal
DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch
-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs
-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C
-DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff
-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning)
-// static analysis
-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor...
-DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
+DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed
DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
@@ -91,7 +59,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <ctime>
#include <cmath>
#include <climits>
-// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37
+// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37
#ifdef __BORLANDC__
#include <math.h>
#endif // __BORLANDC__
@@ -103,20 +71,33 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <utility>
#include <fstream>
#include <sstream>
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <iostream>
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
#include <algorithm>
#include <iomanip>
#include <vector>
+#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
#include <atomic>
#include <mutex>
+#define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
+#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+#define DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_DECLARE_STATIC_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name)
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
#include <set>
#include <map>
+#include <unordered_set>
#include <exception>
#include <stdexcept>
#include <csignal>
#include <cfloat>
#include <cctype>
#include <cstdint>
+#include <string>
#ifdef DOCTEST_PLATFORM_MAC
#include <sys/types.h>
@@ -129,9 +110,11 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
// defines for a leaner windows.h
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
+#define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
+#define DOCTEST_UNDEF_NOMINMAX
#endif // NOMINMAX
// not sure what AfxWin.h is for - here I do what Catch does
@@ -149,7 +132,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#endif // DOCTEST_PLATFORM_WINDOWS
-// this is a fix for https://github.com/onqtam/doctest/issues/348
+// this is a fix for https://github.com/doctest/doctest/issues/348
// https://mail.gnome.org/archives/xml/2012-January/msg00000.html
#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
#define STDOUT_FILENO fileno(stdout)
@@ -171,8 +154,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#endif
#ifndef DOCTEST_THREAD_LOCAL
+#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_THREAD_LOCAL
+#else // DOCTEST_MSVC
#define DOCTEST_THREAD_LOCAL thread_local
-#endif
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_THREAD_LOCAL
#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
@@ -192,12 +179,40 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
#endif
+#ifndef DOCTEST_CDECL
+#define DOCTEST_CDECL __cdecl
+#endif
+
namespace doctest {
bool is_running_in_test = false;
namespace {
using namespace detail;
+
+ template <typename Ex>
+ DOCTEST_NORETURN void throw_exception(Ex const& e) {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ throw e;
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION
+ DOCTEST_CONFIG_HANDLE_EXCEPTION(e);
+#else // DOCTEST_CONFIG_HANDLE_EXCEPTION
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ std::cerr << "doctest will terminate because it needed to throw an exception.\n"
+ << "The message was: " << e.what() << '\n';
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION
+ std::terminate();
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ }
+
+#ifndef DOCTEST_INTERNAL_ERROR
+#define DOCTEST_INTERNAL_ERROR(msg) \
+ throw_exception(std::logic_error( \
+ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
+#endif // DOCTEST_INTERNAL_ERROR
+
// case insensitive strcmp
int stricmp(const char* a, const char* b) {
for(;; a++, b++) {
@@ -207,20 +222,6 @@ namespace {
}
}
- template <typename T>
- String fpToString(T value, int precision) {
- std::ostringstream oss;
- oss << std::setprecision(precision) << std::fixed << value;
- std::string d = oss.str();
- size_t i = d.find_last_not_of('0');
- if(i != std::string::npos && i != d.size() - 1) {
- if(d[i] == '.')
- i++;
- d = d.substr(0, i + 1);
- }
- return d.c_str();
- }
-
struct Endianness
{
enum Arch
@@ -241,58 +242,56 @@ namespace {
} // namespace
namespace detail {
- void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); }
+ DOCTEST_THREAD_LOCAL class
+ {
+ std::vector<std::streampos> stack;
+ std::stringstream ss;
- String rawMemoryToString(const void* object, unsigned size) {
- // Reverse order for little endian architectures
- int i = 0, end = static_cast<int>(size), inc = 1;
- if(Endianness::which() == Endianness::Little) {
- i = end - 1;
- end = inc = -1;
+ public:
+ std::ostream* push() {
+ stack.push_back(ss.tellp());
+ return &ss;
}
- unsigned const char* bytes = static_cast<unsigned const char*>(object);
- std::ostringstream oss;
- oss << "0x" << std::setfill('0') << std::hex;
- for(; i != end; i += inc)
- oss << std::setw(2) << static_cast<unsigned>(bytes[i]);
- return oss.str().c_str();
- }
+ String pop() {
+ if (stack.empty())
+ DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!");
- DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp)
+ std::streampos pos = stack.back();
+ stack.pop_back();
+ unsigned sz = static_cast<unsigned>(ss.tellp() - pos);
+ ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out);
+ return String(ss, sz);
+ }
+ } g_oss;
- std::ostream* getTlsOss() {
- g_oss.clear(); // there shouldn't be anything worth clearing in the flags
- g_oss.str(""); // the slow way of resetting a string stream
- //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383
- return &g_oss;
+ std::ostream* tlssPush() {
+ return g_oss.push();
}
- String getTlsOssResult() {
- //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383
- return g_oss.str().c_str();
+ String tlssPop() {
+ return g_oss.pop();
}
#ifndef DOCTEST_CONFIG_DISABLE
namespace timer_large_integer
{
-
+
#if defined(DOCTEST_PLATFORM_WINDOWS)
- typedef ULONGLONG type;
+ using type = ULONGLONG;
#else // DOCTEST_PLATFORM_WINDOWS
- using namespace std;
- typedef uint64_t type;
+ using type = std::uint64_t;
#endif // DOCTEST_PLATFORM_WINDOWS
}
-typedef timer_large_integer::type ticks_t;
+using ticks_t = timer_large_integer::type;
#ifdef DOCTEST_CONFIG_GETCURRENTTICKS
ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); }
#elif defined(DOCTEST_PLATFORM_WINDOWS)
ticks_t getCurrentTicks() {
- static LARGE_INTEGER hz = {0}, hzo = {0};
+ static LARGE_INTEGER hz = { {0} }, hzo = { {0} };
if(!hz.QuadPart) {
QueryPerformanceFrequency(&hz);
QueryPerformanceCounter(&hzo);
@@ -324,9 +323,17 @@ typedef timer_large_integer::type ticks_t;
ticks_t m_ticks = 0;
};
-#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = T;
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = std::atomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
+
+#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
template <typename T>
- using AtomicOrMultiLaneAtomic = std::atomic<T>;
+ using MultiLaneAtomic = Atomic<T>;
#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
// Provides a multilane implementation of an atomic variable that supports add, sub, load,
// store. Instead of using a single atomic variable, this splits up into multiple ones,
@@ -343,8 +350,8 @@ typedef timer_large_integer::type ticks_t;
{
struct CacheLineAlignedAtomic
{
- std::atomic<T> atomic{};
- char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+ Atomic<T> atomic{};
+ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
};
CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
@@ -374,7 +381,7 @@ typedef timer_large_integer::type ticks_t;
return result;
}
- T operator=(T desired) DOCTEST_NOEXCEPT {
+ T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this]
store(desired);
return desired;
}
@@ -389,7 +396,7 @@ typedef timer_large_integer::type ticks_t;
private:
// Each thread has a different atomic that it operates on. If more than NumLanes threads
- // use this, some will use the same atomic. So performance will degrate a bit, but still
+ // use this, some will use the same atomic. So performance will degrade a bit, but still
// everything will work.
//
// The logic here is a bit tricky. The call should be as fast as possible, so that there
@@ -400,24 +407,21 @@ typedef timer_large_integer::type ticks_t;
// assigned in a round-robin fashion.
// 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
// little overhead.
- std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
- static std::atomic<size_t> laneCounter;
+ Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+ static Atomic<size_t> laneCounter;
DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
return m_atomics[tlsLaneIdx].atomic;
}
};
-
- template <typename T>
- using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
// this holds both parameters from the command line and runtime data for tests
struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
{
- AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
- AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+ MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+ MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
@@ -430,11 +434,12 @@ typedef timer_large_integer::type ticks_t;
std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
// stuff for subcases
- std::vector<SubcaseSignature> subcasesStack;
- std::set<decltype(subcasesStack)> subcasesPassed;
- int subcasesCurrentMaxLevel;
- bool should_reenter;
- std::atomic<bool> shouldLogCurrentException;
+ bool reachedLeaf;
+ std::vector<SubcaseSignature> subcaseStack;
+ std::vector<SubcaseSignature> nextSubcaseStack;
+ std::unordered_set<unsigned long long> fullyTraversedSubcases;
+ size_t currentSubcaseDepth;
+ Atomic<bool> shouldLogCurrentException;
void resetRunData() {
numTestCases = 0;
@@ -484,7 +489,8 @@ typedef timer_large_integer::type ticks_t;
(TestCaseFailureReason::FailedExactlyNumTimes & failure_flags);
// if any subcase has failed - the whole test case has failed
- if(failure_flags && !ok_to_fail)
+ testCaseSuccess = !(failure_flags && !ok_to_fail);
+ if(!testCaseSuccess)
numTestCasesFailed++;
}
};
@@ -499,23 +505,37 @@ typedef timer_large_integer::type ticks_t;
#endif // DOCTEST_CONFIG_DISABLE
} // namespace detail
-void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
-void String::setLast(unsigned in) { buf[last] = char(in); }
+char* String::allocate(size_type sz) {
+ if (sz <= last) {
+ buf[sz] = '\0';
+ setLast(last - sz);
+ return buf;
+ } else {
+ setOnHeap();
+ data.size = sz;
+ data.capacity = data.size + 1;
+ data.ptr = new char[data.capacity];
+ data.ptr[sz] = '\0';
+ return data.ptr;
+ }
+}
+
+void String::setOnHeap() noexcept { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
+void String::setLast(size_type in) noexcept { buf[last] = char(in); }
+void String::setSize(size_type sz) noexcept {
+ if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); }
+ else { data.ptr[sz] = '\0'; data.size = sz; }
+}
void String::copy(const String& other) {
- using namespace std;
if(other.isOnStack()) {
memcpy(buf, other.buf, len);
} else {
- setOnHeap();
- data.size = other.data.size;
- data.capacity = data.size + 1;
- data.ptr = new char[data.capacity];
- memcpy(data.ptr, other.data.ptr, data.size + 1);
+ memcpy(allocate(other.data.size), other.data.ptr, other.data.size);
}
}
-String::String() {
+String::String() noexcept {
buf[0] = '\0';
setLast();
}
@@ -523,26 +543,17 @@ String::String() {
String::~String() {
if(!isOnStack())
delete[] data.ptr;
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
-}
+} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
String::String(const char* in)
: String(in, strlen(in)) {}
-String::String(const char* in, unsigned in_size) {
- using namespace std;
- if(in_size <= last) {
- memcpy(buf, in, in_size);
- buf[in_size] = '\0';
- setLast(last - in_size);
- } else {
- setOnHeap();
- data.size = in_size;
- data.capacity = data.size + 1;
- data.ptr = new char[data.capacity];
- memcpy(data.ptr, in, in_size);
- data.ptr[in_size] = '\0';
- }
+String::String(const char* in, size_type in_size) {
+ memcpy(allocate(in_size), in, in_size);
+}
+
+String::String(std::istream& in, size_type in_size) {
+ in.read(allocate(in_size), in_size);
}
String::String(const String& other) { copy(other); }
@@ -559,10 +570,9 @@ String& String::operator=(const String& other) {
}
String& String::operator+=(const String& other) {
- const unsigned my_old_size = size();
- const unsigned other_size = other.size();
- const unsigned total_size = my_old_size + other_size;
- using namespace std;
+ const size_type my_old_size = size();
+ const size_type other_size = other.size();
+ const size_type total_size = my_old_size + other_size;
if(isOnStack()) {
if(total_size < len) {
// append to the current stack space
@@ -609,18 +619,13 @@ String& String::operator+=(const String& other) {
return *this;
}
-// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
-String String::operator+(const String& other) const { return String(*this) += other; }
-
-String::String(String&& other) {
- using namespace std;
+String::String(String&& other) noexcept {
memcpy(buf, other.buf, len);
other.buf[0] = '\0';
other.setLast();
}
-String& String::operator=(String&& other) {
- using namespace std;
+String& String::operator=(String&& other) noexcept {
if(this != &other) {
if(!isOnStack())
delete[] data.ptr;
@@ -631,30 +636,60 @@ String& String::operator=(String&& other) {
return *this;
}
-char String::operator[](unsigned i) const {
- return const_cast<String*>(this)->operator[](i); // NOLINT
+char String::operator[](size_type i) const {
+ return const_cast<String*>(this)->operator[](i);
}
-char& String::operator[](unsigned i) {
+char& String::operator[](size_type i) {
if(isOnStack())
return reinterpret_cast<char*>(buf)[i];
return data.ptr[i];
}
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized")
-unsigned String::size() const {
+String::size_type String::size() const {
if(isOnStack())
- return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32
+ return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32
return data.size;
}
DOCTEST_GCC_SUPPRESS_WARNING_POP
-unsigned String::capacity() const {
+String::size_type String::capacity() const {
if(isOnStack())
return len;
return data.capacity;
}
+String String::substr(size_type pos, size_type cnt) && {
+ cnt = std::min(cnt, size() - 1 - pos);
+ char* cptr = c_str();
+ memmove(cptr, cptr + pos, cnt);
+ setSize(cnt);
+ return std::move(*this);
+}
+
+String String::substr(size_type pos, size_type cnt) const & {
+ cnt = std::min(cnt, size() - 1 - pos);
+ return String{ c_str() + pos, cnt };
+}
+
+String::size_type String::find(char ch, size_type pos) const {
+ const char* begin = c_str();
+ const char* end = begin + size();
+ const char* it = begin + pos;
+ for (; it < end && *it != ch; it++);
+ if (it < end) { return static_cast<size_type>(it - begin); }
+ else { return npos; }
+}
+
+String::size_type String::rfind(char ch, size_type pos) const {
+ const char* begin = c_str();
+ const char* it = begin + std::min(pos, size() - 1);
+ for (; it >= begin && *it != ch; it--);
+ if (it >= begin) { return static_cast<size_type>(it - begin); }
+ else { return npos; }
+}
+
int String::compare(const char* other, bool no_case) const {
if(no_case)
return doctest::stricmp(c_str(), other);
@@ -665,17 +700,32 @@ int String::compare(const String& other, bool no_case) const {
return compare(other.c_str(), no_case);
}
-// clang-format off
+String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; }
+
bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; }
bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; }
-// clang-format on
std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); }
+Contains::Contains(const String& str) : string(str) { }
+
+bool Contains::checkWith(const String& other) const {
+ return strstr(other.c_str(), string.c_str()) != nullptr;
+}
+
+String toString(const Contains& in) {
+ return "Contains( " + in.string + " )";
+}
+
+bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); }
+bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); }
+bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); }
+bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); }
+
namespace {
void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;)
} // namespace
@@ -689,64 +739,42 @@ namespace Color {
// clang-format off
const char* assertString(assertType::Enum at) {
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled
- switch(at) { //!OCLINT missing default in switch statements
- case assertType::DT_WARN : return "WARN";
- case assertType::DT_CHECK : return "CHECK";
- case assertType::DT_REQUIRE : return "REQUIRE";
-
- case assertType::DT_WARN_FALSE : return "WARN_FALSE";
- case assertType::DT_CHECK_FALSE : return "CHECK_FALSE";
- case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE";
-
- case assertType::DT_WARN_THROWS : return "WARN_THROWS";
- case assertType::DT_CHECK_THROWS : return "CHECK_THROWS";
- case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS";
-
- case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS";
- case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS";
- case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS";
-
- case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH";
- case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH";
- case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH";
-
- case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS";
- case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS";
- case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS";
-
- case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW";
- case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW";
- case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW";
-
- case assertType::DT_WARN_EQ : return "WARN_EQ";
- case assertType::DT_CHECK_EQ : return "CHECK_EQ";
- case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ";
- case assertType::DT_WARN_NE : return "WARN_NE";
- case assertType::DT_CHECK_NE : return "CHECK_NE";
- case assertType::DT_REQUIRE_NE : return "REQUIRE_NE";
- case assertType::DT_WARN_GT : return "WARN_GT";
- case assertType::DT_CHECK_GT : return "CHECK_GT";
- case assertType::DT_REQUIRE_GT : return "REQUIRE_GT";
- case assertType::DT_WARN_LT : return "WARN_LT";
- case assertType::DT_CHECK_LT : return "CHECK_LT";
- case assertType::DT_REQUIRE_LT : return "REQUIRE_LT";
- case assertType::DT_WARN_GE : return "WARN_GE";
- case assertType::DT_CHECK_GE : return "CHECK_GE";
- case assertType::DT_REQUIRE_GE : return "REQUIRE_GE";
- case assertType::DT_WARN_LE : return "WARN_LE";
- case assertType::DT_CHECK_LE : return "CHECK_LE";
- case assertType::DT_REQUIRE_LE : return "REQUIRE_LE";
-
- case assertType::DT_WARN_UNARY : return "WARN_UNARY";
- case assertType::DT_CHECK_UNARY : return "CHECK_UNARY";
- case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY";
- case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE";
- case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE";
- case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE";
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled
+ #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
+ #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type)
+ switch(at) {
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE);
+
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY);
+ DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE);
+
+ default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!");
}
DOCTEST_MSVC_SUPPRESS_WARNING_POP
- return "";
}
// clang-format on
@@ -780,6 +808,12 @@ const char* skipPathFromFilename(const char* file) {
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+bool SubcaseSignature::operator==(const SubcaseSignature& other) const {
+ return m_line == other.m_line
+ && std::strcmp(m_file, other.m_file) == 0
+ && m_name == other.m_name;
+}
+
bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
if(m_line != other.m_line)
return m_line < other.m_line;
@@ -788,45 +822,53 @@ bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
return m_name.compare(other.m_name) < 0;
}
-IContextScope::IContextScope() = default;
-IContextScope::~IContextScope() = default;
+DOCTEST_DEFINE_INTERFACE(IContextScope)
+
+namespace detail {
+ void filldata<const void*>::fill(std::ostream* stream, const void* in) {
+ if (in) { *stream << in; }
+ else { *stream << "nullptr"; }
+ }
+
+ template <typename T>
+ String toStreamLit(T t) {
+ std::ostream* os = tlssPush();
+ os->operator<<(t);
+ return tlssPop();
+ }
+}
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-String toString(char* in) { return toString(static_cast<const char*>(in)); }
-// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-String toString(bool in) { return in ? "true" : "false"; }
-String toString(float in) { return fpToString(in, 5) + "f"; }
-String toString(double in) { return fpToString(in, 10); }
-String toString(double long in) { return fpToString(in, 15); }
-
-#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \
- String toString(type in) { \
- char buf[64]; \
- std::sprintf(buf, fmt, in); \
- return buf; \
- }
-
-DOCTEST_TO_STRING_OVERLOAD(char, "%d")
-DOCTEST_TO_STRING_OVERLOAD(char signed, "%d")
-DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int short, "%d")
-DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int, "%d")
-DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u")
-DOCTEST_TO_STRING_OVERLOAD(int long, "%ld")
-DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu")
-DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld")
-DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu")
-
-String toString(std::nullptr_t) { return "NULL"; }
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
String toString(const std::string& in) { return in.c_str(); }
#endif // VS 2019
+String toString(String in) { return in; }
+
+String toString(std::nullptr_t) { return "nullptr"; }
+
+String toString(bool in) { return in ? "true" : "false"; }
+
+String toString(float in) { return toStreamLit(in); }
+String toString(double in) { return toStreamLit(in); }
+String toString(double long in) { return toStreamLit(in); }
+
+String toString(char in) { return toStreamLit(static_cast<signed>(in)); }
+String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); }
+String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(in)); }
+String toString(short in) { return toStreamLit(in); }
+String toString(short unsigned in) { return toStreamLit(in); }
+String toString(signed in) { return toStreamLit(in); }
+String toString(unsigned in) { return toStreamLit(in); }
+String toString(long in) { return toStreamLit(in); }
+String toString(long unsigned in) { return toStreamLit(in); }
+String toString(long long in) { return toStreamLit(in); }
+String toString(long long unsigned in) { return toStreamLit(in); }
+
Approx::Approx(double value)
: m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100)
, m_scale(1.0)
@@ -866,11 +908,25 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs
bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
String toString(const Approx& in) {
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
- return String("Approx( ") + doctest::toString(in.m_value) + " )";
+ return "Approx( " + doctest::toString(in.m_value) + " )";
}
const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); }
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
+template <typename F>
+IsNaN<F>::operator bool() const {
+ return std::isnan(value) ^ flipped;
+}
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
+template <typename F>
+String toString(IsNaN<F> in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; }
+String toString(IsNaN<float> in) { return toString<float>(in); }
+String toString(IsNaN<double> in) { return toString<double>(in); }
+String toString(IsNaN<double long> in) { return toString<double long>(in); }
+
} // namespace doctest
#ifdef DOCTEST_CONFIG_DISABLE
@@ -880,15 +936,15 @@ Context::~Context() = default;
void Context::applyCommandLine(int, const char* const*) {}
void Context::addFilter(const char*, const char*) {}
void Context::clearFilters() {}
+void Context::setOption(const char*, bool) {}
void Context::setOption(const char*, int) {}
void Context::setOption(const char*, const char*) {}
bool Context::shouldExit() { return false; }
void Context::setAsDefaultForAssertsOutOfTestCases() {}
void Context::setAssertHandler(detail::assert_handler) {}
+void Context::setCout(std::ostream*) {}
int Context::run() { return 0; }
-IReporter::~IReporter() = default;
-
int IReporter::get_num_active_contexts() { return 0; }
const IContextScope* const* IReporter::get_active_contexts() { return nullptr; }
int IReporter::get_num_stringified_contexts() { return 0; }
@@ -921,7 +977,7 @@ namespace doctest {
namespace {
// the int (priority) is part of the key for automatic sorting - sadly one can register a
// reporter with a duplicate name and a different priority but hopefully that won't happen often :|
- typedef std::map<std::pair<int, String>, reporterCreatorFunc> reporterMap;
+ using reporterMap = std::map<std::pair<int, String>, reporterCreatorFunc>;
reporterMap& getReporters() {
static reporterMap data;
@@ -953,8 +1009,8 @@ namespace detail {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
DOCTEST_NORETURN void throwException() {
g_cs->shouldLogCurrentException = false;
- throw TestFailureException();
- } // NOLINT(cert-err60-cpp)
+ throw TestFailureException(); // NOLINT(hicpp-exception-baseclass)
+ }
#else // DOCTEST_CONFIG_NO_EXCEPTIONS
void throwException() {}
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -1000,91 +1056,134 @@ namespace {
return !*wild;
}
- //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
- //unsigned hashStr(unsigned const char* str) {
- // unsigned long hash = 5381;
- // char c;
- // while((c = *str++))
- // hash = ((hash << 5) + hash) + c; // hash * 33 + c
- // return hash;
- //}
-
// checks if the name matches any of the filters (and can be configured what to do when empty)
bool matchesAny(const char* name, const std::vector<String>& filters, bool matchEmpty,
- bool caseSensitive) {
- if(filters.empty() && matchEmpty)
+ bool caseSensitive) {
+ if (filters.empty() && matchEmpty)
return true;
- for(auto& curr : filters)
- if(wildcmp(name, curr.c_str(), caseSensitive))
+ for (auto& curr : filters)
+ if (wildcmp(name, curr.c_str(), caseSensitive))
return true;
return false;
}
-} // namespace
-namespace detail {
- Subcase::Subcase(const String& name, const char* file, int line)
- : m_signature({name, file, line}) {
- auto* s = g_cs;
+ DOCTEST_NO_SANITIZE_INTEGER
+ unsigned long long hash(unsigned long long a, unsigned long long b) {
+ return (a << 5) + b;
+ }
- // check subcase filters
- if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) {
- if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive))
- return;
- if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive))
- return;
- }
-
- // if a Subcase on the same level has already been entered
- if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) {
- s->should_reenter = true;
- return;
- }
+ // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
+ DOCTEST_NO_SANITIZE_INTEGER
+ unsigned long long hash(const char* str) {
+ unsigned long long hash = 5381;
+ char c;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) + c; // hash * 33 + c
+ return hash;
+ }
- // push the current signature to the stack so we can check if the
- // current stack + the current new subcase have been traversed
- s->subcasesStack.push_back(m_signature);
- if(s->subcasesPassed.count(s->subcasesStack) != 0) {
- // pop - revert to previous stack since we've already passed this
- s->subcasesStack.pop_back();
- return;
+ unsigned long long hash(const SubcaseSignature& sig) {
+ return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line);
+ }
+
+ unsigned long long hash(const std::vector<SubcaseSignature>& sigs, size_t count) {
+ unsigned long long running = 0;
+ auto end = sigs.begin() + count;
+ for (auto it = sigs.begin(); it != end; it++) {
+ running = hash(running, hash(*it));
}
+ return running;
+ }
- s->subcasesCurrentMaxLevel = s->subcasesStack.size();
- m_entered = true;
+ unsigned long long hash(const std::vector<SubcaseSignature>& sigs) {
+ unsigned long long running = 0;
+ for (const SubcaseSignature& sig : sigs) {
+ running = hash(running, hash(sig));
+ }
+ return running;
+ }
+} // namespace
+namespace detail {
+ bool Subcase::checkFilters() {
+ if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) {
+ if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive))
+ return true;
+ if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive))
+ return true;
+ }
+ return false;
+ }
- DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ Subcase::Subcase(const String& name, const char* file, int line)
+ : m_signature({name, file, line}) {
+ if (!g_cs->reachedLeaf) {
+ if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size()
+ || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) {
+ // Going down.
+ if (checkFilters()) { return; }
+
+ g_cs->subcaseStack.push_back(m_signature);
+ g_cs->currentSubcaseDepth++;
+ m_entered = true;
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ }
+ } else {
+ if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) {
+ // This subcase is reentered via control flow.
+ g_cs->currentSubcaseDepth++;
+ m_entered = true;
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
+ } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth
+ && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature)))
+ == g_cs->fullyTraversedSubcases.end()) {
+ if (checkFilters()) { return; }
+ // This subcase is part of the one to be executed next.
+ g_cs->nextSubcaseStack.clear();
+ g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(),
+ g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth);
+ g_cs->nextSubcaseStack.push_back(m_signature);
+ }
+ }
}
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
- DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
Subcase::~Subcase() {
- if(m_entered) {
- // only mark the subcase stack as passed if no subcases have been skipped
- if(g_cs->should_reenter == false)
- g_cs->subcasesPassed.insert(g_cs->subcasesStack);
- g_cs->subcasesStack.pop_back();
+ if (m_entered) {
+ g_cs->currentSubcaseDepth--;
+
+ if (!g_cs->reachedLeaf) {
+ // Leaf.
+ g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
+ g_cs->nextSubcaseStack.clear();
+ g_cs->reachedLeaf = true;
+ } else if (g_cs->nextSubcaseStack.empty()) {
+ // All children are finished.
+ g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
+ }
#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0
#else
if(std::uncaught_exception()
#endif
- && g_cs->shouldLogCurrentException) {
+ && g_cs->shouldLogCurrentException) {
DOCTEST_ITERATE_THROUGH_REPORTERS(
test_case_exception, {"exception thrown in subcase - will translate later "
- "when the whole test case has been exited (cannot "
- "translate while there is an active exception)",
- false});
+ "when the whole test case has been exited (cannot "
+ "translate while there is an active exception)",
+ false});
g_cs->shouldLogCurrentException = false;
}
+
DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
}
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
- DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
Subcase::operator bool() const { return m_entered; }
@@ -1098,20 +1197,11 @@ namespace detail {
TestSuite& TestSuite::operator*(const char* in) {
m_test_suite = in;
- // clear state
- m_description = nullptr;
- m_skip = false;
- m_no_breaks = false;
- m_no_output = false;
- m_may_fail = false;
- m_should_fail = false;
- m_expected_failures = 0;
- m_timeout = 0;
return *this;
}
TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
- const char* type, int template_id) {
+ const String& type, int template_id) {
m_file = file;
m_line = line;
m_name = nullptr; // will be later overridden in operator*
@@ -1136,10 +1226,8 @@ namespace detail {
}
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
- DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice
TestCase& TestCase::operator=(const TestCase& other) {
- static_cast<TestCaseData&>(*this) = static_cast<const TestCaseData&>(other);
-
+ TestCaseData::operator=(other);
m_test = other.m_test;
m_type = other.m_type;
m_template_id = other.m_template_id;
@@ -1155,7 +1243,7 @@ namespace detail {
m_name = in;
// make a new name with an appended type for templated test case
if(m_template_id != -1) {
- m_full_name = String(m_name) + m_type;
+ m_full_name = String(m_name) + "<" + m_type + ">";
// redirect the name to point to the newly constructed full name
m_name = m_full_name.c_str();
}
@@ -1211,29 +1299,6 @@ namespace {
return suiteOrderComparator(lhs, rhs);
}
-#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
- HANDLE g_stdoutHandle;
- WORD g_origFgAttrs;
- WORD g_origBgAttrs;
- bool g_attrsInitted = false;
-
- int colors_init() {
- if(!g_attrsInitted) {
- g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
- g_attrsInitted = true;
- CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
- GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo);
- g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
- BACKGROUND_BLUE | BACKGROUND_INTENSITY);
- g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
- FOREGROUND_BLUE | FOREGROUND_INTENSITY);
- }
- return 0;
- }
-
- int dumy_init_console_colors = colors_init();
-#endif // DOCTEST_CONFIG_COLORS_WINDOWS
-
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
void color_to_stream(std::ostream& s, Color::Enum code) {
static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
@@ -1267,10 +1332,26 @@ namespace {
#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
if(g_no_colors ||
- (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false))
+ (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
return;
-#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs)
+ static struct ConsoleHelper {
+ HANDLE stdoutHandle;
+ WORD origFgAttrs;
+ WORD origBgAttrs;
+
+ ConsoleHelper() {
+ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
+ origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
+ BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+ origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ }
+ } ch;
+
+#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs)
// clang-format off
switch (code) {
@@ -1287,7 +1368,7 @@ namespace {
case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
case Color::None:
case Color::Bright: // invalid
- default: DOCTEST_SET_ATTR(g_origFgAttrs);
+ default: DOCTEST_SET_ATTR(ch.origFgAttrs);
}
// clang-format on
#endif // DOCTEST_CONFIG_COLORS_WINDOWS
@@ -1404,35 +1485,22 @@ namespace detail {
getExceptionTranslators().push_back(et);
}
-#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- void toStream(std::ostream* s, char* in) { *s << in; }
- void toStream(std::ostream* s, const char* in) { *s << in; }
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; }
- void toStream(std::ostream* s, float in) { *s << in; }
- void toStream(std::ostream* s, double in) { *s << in; }
- void toStream(std::ostream* s, double long in) { *s << in; }
-
- void toStream(std::ostream* s, char in) { *s << in; }
- void toStream(std::ostream* s, char signed in) { *s << in; }
- void toStream(std::ostream* s, char unsigned in) { *s << in; }
- void toStream(std::ostream* s, int short in) { *s << in; }
- void toStream(std::ostream* s, int short unsigned in) { *s << in; }
- void toStream(std::ostream* s, int in) { *s << in; }
- void toStream(std::ostream* s, int unsigned in) { *s << in; }
- void toStream(std::ostream* s, int long in) { *s << in; }
- void toStream(std::ostream* s, int long unsigned in) { *s << in; }
- void toStream(std::ostream* s, int long long in) { *s << in; }
- void toStream(std::ostream* s, int long long unsigned in) { *s << in; }
-
DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO()
ContextScopeBase::ContextScopeBase() {
g_infoContexts.push_back(this);
}
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
- DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept {
+ if (other.need_to_destroy) {
+ other.destroy();
+ }
+ other.need_to_destroy = false;
+ g_infoContexts.push_back(this);
+ }
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
// destroy cannot be inlined into the destructor because that would mean calling stringify after
@@ -1451,8 +1519,8 @@ namespace detail {
g_infoContexts.pop_back();
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
- DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
} // namespace detail
namespace {
@@ -1493,10 +1561,10 @@ namespace {
static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
// Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
// console just once no matter how many threads have crashed.
- static std::mutex mutex;
+ DOCTEST_DECLARE_STATIC_MUTEX(mutex)
static bool execute = true;
{
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
if(execute) {
bool reported = false;
for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
@@ -1599,7 +1667,7 @@ namespace {
static unsigned int prev_abort_behavior;
static int prev_report_mode;
static _HFILE prev_report_file;
- static void (*prev_sigabrt_handler)(int);
+ static void (DOCTEST_CDECL *prev_sigabrt_handler)(int);
static std::terminate_handler original_terminate_handler;
static bool isSet;
static ULONG guaranteeSize;
@@ -1611,7 +1679,7 @@ namespace {
unsigned int FatalConditionHandler::prev_abort_behavior;
int FatalConditionHandler::prev_report_mode;
_HFILE FatalConditionHandler::prev_report_file;
- void (*FatalConditionHandler::prev_sigabrt_handler)(int);
+ void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int);
std::terminate_handler FatalConditionHandler::original_terminate_handler;
bool FatalConditionHandler::isSet = false;
ULONG FatalConditionHandler::guaranteeSize = 0;
@@ -1669,7 +1737,7 @@ namespace {
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = {};
- sa.sa_handler = handleSignal; // NOLINT
+ sa.sa_handler = handleSignal;
sa.sa_flags = SA_ONSTACK;
for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
@@ -1708,7 +1776,7 @@ namespace {
#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text)
#else
// TODO: integration with XCode and other IDEs
-#define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros)
+#define DOCTEST_OUTPUT_DEBUG_STRING(text)
#endif // Platform
void addAssert(assertType::Enum at) {
@@ -1727,8 +1795,8 @@ namespace {
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true});
- while(g_cs->subcasesStack.size()) {
- g_cs->subcasesStack.pop_back();
+ while (g_cs->subcaseStack.size()) {
+ g_cs->subcaseStack.pop_back();
DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
}
@@ -1740,25 +1808,26 @@ namespace {
}
#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
} // namespace
-namespace detail {
- ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
- const char* exception_type, const char* exception_string) {
- m_test_case = g_cs->currentTest;
- m_at = at;
- m_file = file;
- m_line = line;
- m_expr = expr;
- m_failed = true;
- m_threw = false;
- m_threw_as = false;
- m_exception_type = exception_type;
- m_exception_string = exception_string;
+AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string)
+ : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr),
+ m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type),
+ m_exception_string(exception_string) {
#if DOCTEST_MSVC
- if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
- ++m_expr;
+ if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
+ ++m_expr;
#endif // MSVC
- }
+}
+
+namespace detail {
+ ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const String& exception_string)
+ : AssertData(at, file, line, expr, exception_type, exception_string) { }
+
+ ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string)
+ : AssertData(at, file, line, expr, exception_type, exception_string) { }
void ResultBuilder::setResult(const Result& res) {
m_decomp = res.m_decomp;
@@ -1774,17 +1843,17 @@ namespace detail {
if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
m_failed = !m_threw;
} else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT
- m_failed = !m_threw_as || (m_exception != m_exception_string);
+ m_failed = !m_threw_as || !m_exception_string.check(m_exception);
} else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
m_failed = !m_threw_as;
} else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
- m_failed = m_exception != m_exception_string;
+ m_failed = !m_exception_string.check(m_exception);
} else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
m_failed = m_threw;
}
if(m_exception.size())
- m_exception = String("\"") + m_exception + "\"";
+ m_exception = "\"" + m_exception + "\"";
if(is_running_in_test) {
addAssert(m_at);
@@ -1812,8 +1881,8 @@ namespace detail {
std::abort();
}
- void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
- Result result) {
+ bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
+ const Result& result) {
bool failed = !result.m_passed;
// ###################################################################################
@@ -1822,21 +1891,29 @@ namespace detail {
// ###################################################################################
DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp);
DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ return !failed;
}
MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
- m_stream = getTlsOss();
+ m_stream = tlssPush();
m_file = file;
m_line = line;
m_severity = severity;
}
- IExceptionTranslator::IExceptionTranslator() = default;
- IExceptionTranslator::~IExceptionTranslator() = default;
+ MessageBuilder::~MessageBuilder() {
+ if (!logged)
+ tlssPop();
+ }
+
+ DOCTEST_DEFINE_INTERFACE(IExceptionTranslator)
bool MessageBuilder::log() {
- m_string = getTlsOssResult();
+ if (!logged) {
+ m_string = tlssPop();
+ logged = true;
+ }
+
DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
const bool isWarn = m_severity & assertType::is_warn;
@@ -1855,29 +1932,10 @@ namespace detail {
if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional
throwException();
}
-
- MessageBuilder::~MessageBuilder() = default;
} // namespace detail
namespace {
using namespace detail;
- template <typename Ex>
- DOCTEST_NORETURN void throw_exception(Ex const& e) {
-#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
- throw e;
-#else // DOCTEST_CONFIG_NO_EXCEPTIONS
- std::cerr << "doctest will terminate because it needed to throw an exception.\n"
- << "The message was: " << e.what() << '\n';
- std::terminate();
-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
- }
-
-#ifndef DOCTEST_INTERNAL_ERROR
-#define DOCTEST_INTERNAL_ERROR(msg) \
- throw_exception(std::logic_error( \
- __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
-#endif // DOCTEST_INTERNAL_ERROR
-
// clang-format off
// =================================================================================================
@@ -1924,7 +1982,11 @@ namespace {
mutable XmlWriter* m_writer = nullptr;
};
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
XmlWriter( std::ostream& os = std::cout );
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ XmlWriter( std::ostream& os );
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
~XmlWriter();
XmlWriter( XmlWriter const& ) = delete;
@@ -1959,10 +2021,10 @@ namespace {
void ensureTagClosed();
- private:
-
void writeDeclaration();
+ private:
+
void newlineIfNecessary();
bool m_tagIsOpen = false;
@@ -2151,7 +2213,7 @@ namespace {
XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
{
- writeDeclaration();
+ // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627
}
XmlWriter::~XmlWriter() {
@@ -2262,8 +2324,8 @@ namespace {
struct XmlReporter : public IReporter
{
- XmlWriter xml;
- std::mutex mutex;
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
@@ -2340,7 +2402,8 @@ namespace {
xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name)
.writeAttribute("testsuite", in.data[i]->m_test_suite)
.writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str()))
- .writeAttribute("line", line(in.data[i]->m_line));
+ .writeAttribute("line", line(in.data[i]->m_line))
+ .writeAttribute("skipped", in.data[i]->m_skip);
}
xml.scopedElement("OverallResultsTestCases")
.writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
@@ -2356,6 +2419,8 @@ namespace {
}
void test_run_start() override {
+ xml.writeDeclaration();
+
// remove .exe extension - mainly to have the same output on UNIX and Windows
std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
#ifdef DOCTEST_PLATFORM_WINDOWS
@@ -2403,14 +2468,15 @@ namespace {
test_case_start_impl(in);
xml.ensureTagClosed();
}
-
+
void test_case_reenter(const TestCaseData&) override {}
void test_case_end(const CurrentTestCaseStats& st) override {
xml.startElement("OverallResultsAsserts")
.writeAttribute("successes",
st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest)
- .writeAttribute("failures", st.numAssertsFailedCurrentTest);
+ .writeAttribute("failures", st.numAssertsFailedCurrentTest)
+ .writeAttribute("test_case_success", st.testCaseSuccess);
if(opt.duration)
xml.writeAttribute("duration", st.seconds);
if(tc->m_expected_failures)
@@ -2421,7 +2487,7 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.scopedElement("Exception")
.writeAttribute("crash", e.is_crash)
@@ -2429,8 +2495,6 @@ namespace {
}
void subcase_start(const SubcaseSignature& in) override {
- std::lock_guard<std::mutex> lock(mutex);
-
xml.startElement("SubCase")
.writeAttribute("name", in.m_name)
.writeAttribute("filename", skipPathFromFilename(in.m_file))
@@ -2444,7 +2508,7 @@ namespace {
if(!rb.m_failed && !opt.success)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.startElement("Expression")
.writeAttribute("success", !rb.m_failed)
@@ -2460,7 +2524,7 @@ namespace {
if(rb.m_at & assertType::is_throws_as)
xml.scopedElement("ExpectedException").writeText(rb.m_exception_type);
if(rb.m_at & assertType::is_throws_with)
- xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string);
+ xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str());
if((rb.m_at & assertType::is_normal) && !rb.m_threw)
xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str());
@@ -2470,7 +2534,7 @@ namespace {
}
void log_message(const MessageData& mb) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
xml.startElement("Message")
.writeAttribute("type", failureString(mb.m_severity))
@@ -2506,7 +2570,8 @@ namespace {
} else if((rb.m_at & assertType::is_throws_as) &&
(rb.m_at & assertType::is_throws_with)) { //!OCLINT
s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None;
+ << rb.m_exception_string.c_str()
+ << "\", " << rb.m_exception_type << " ) " << Color::None;
if(rb.m_threw) {
if(!rb.m_failed) {
s << "threw as expected!\n";
@@ -2527,7 +2592,8 @@ namespace {
} else if(rb.m_at &
assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\" ) " << Color::None
+ << rb.m_exception_string.c_str()
+ << "\" ) " << Color::None
<< (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
"threw a DIFFERENT exception: ") :
"did NOT throw at all!")
@@ -2552,8 +2618,8 @@ namespace {
// - more attributes in tags
struct JUnitReporter : public IReporter
{
- XmlWriter xml;
- std::mutex mutex;
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
Timer timer;
std::vector<String> deepestSubcaseStackNames;
@@ -2649,9 +2715,13 @@ namespace {
// WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
// =========================================================================================
- void report_query(const QueryData&) override {}
+ void report_query(const QueryData&) override {
+ xml.writeDeclaration();
+ }
- void test_run_start() override {}
+ void test_run_start() override {
+ xml.writeDeclaration();
+ }
void test_run_end(const TestRunStats& p) override {
// remove .exe extension - mainly to have the same output on UNIX and Windows
@@ -2721,12 +2791,11 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
testCaseData.addError("exception", e.error_string.c_str());
}
void subcase_start(const SubcaseSignature& in) override {
- std::lock_guard<std::mutex> lock(mutex);
deepestSubcaseStackNames.push_back(in.m_name);
}
@@ -2736,7 +2805,7 @@ namespace {
if(!rb.m_failed) // report only failures & ignore the `success` option
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
std::ostringstream os;
os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
@@ -2747,7 +2816,22 @@ namespace {
testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
}
- void log_message(const MessageData&) override {}
+ void log_message(const MessageData& mb) override {
+ if(mb.m_severity & assertType::is_warn) // report only failures
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ std::ostringstream os;
+ os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(")
+ << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
+
+ os << mb.m_string.c_str() << "\n";
+ log_contexts(os);
+
+ testCaseData.addFailure(mb.m_string.c_str(),
+ mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str());
+ }
void test_case_skipped(const TestCaseData&) override {}
@@ -2787,7 +2871,7 @@ namespace {
bool hasLoggedCurrentTestStart;
std::vector<SubcaseSignature> subcasesStack;
size_t currentSubcaseLevel;
- std::mutex mutex;
+ DOCTEST_DECLARE_MUTEX(mutex)
// caching pointers/references to objects of these types - safe to do
const ContextOptions& opt;
@@ -2892,9 +2976,11 @@ namespace {
}
void printIntro() {
- printVersion();
- s << Color::Cyan << "[doctest] " << Color::None
- << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
+ if(opt.no_intro == false) {
+ printVersion();
+ s << Color::Cyan << "[doctest] " << Color::None
+ << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
+ }
}
void printHelp() {
@@ -2979,12 +3065,18 @@ namespace {
<< Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "no console output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> "
<< Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> "
@@ -3022,22 +3114,6 @@ namespace {
printReporters(getReporters(), "reporters");
}
- void list_query_results() {
- separator_to_stream();
- if(opt.count || opt.list_test_cases) {
- s << Color::Cyan << "[doctest] " << Color::None
- << "unskipped test cases passing the current filters: "
- << g_cs->numTestCasesPassingFilters << "\n";
- } else if(opt.list_test_suites) {
- s << Color::Cyan << "[doctest] " << Color::None
- << "unskipped test cases passing the current filters: "
- << g_cs->numTestCasesPassingFilters << "\n";
- s << Color::Cyan << "[doctest] " << Color::None
- << "test suites with unskipped test cases passing the current filters: "
- << g_cs->numTestSuitesPassingFilters << "\n";
- }
- }
-
// =========================================================================================
// WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
// =========================================================================================
@@ -3083,15 +3159,21 @@ namespace {
}
}
- void test_run_start() override { printIntro(); }
+ void test_run_start() override {
+ if(!opt.minimal)
+ printIntro();
+ }
void test_run_end(const TestRunStats& p) override {
+ if(opt.minimal && p.numTestCasesFailed == 0)
+ return;
+
separator_to_stream();
s << std::dec;
- auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
- auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
- auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
+ auto totwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
+ auto passwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
+ auto failwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
<< p.numTestCasesPassingFilters << " | "
@@ -3123,7 +3205,7 @@ namespace {
subcasesStack.clear();
currentSubcaseLevel = 0;
}
-
+
void test_case_reenter(const TestCaseData&) override {
subcasesStack.clear();
}
@@ -3135,7 +3217,7 @@ namespace {
// log the preamble of the test case only if there is something
// else to print - something other than that an assert has failed
if(opt.duration ||
- (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure))
+ (st.failure_flags && st.failure_flags != static_cast<int>(TestCaseFailureReason::AssertFailure)))
logTestStart();
if(opt.duration)
@@ -3166,6 +3248,7 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
+ DOCTEST_LOCK_MUTEX(mutex)
if(tc->m_no_output)
return;
@@ -3190,14 +3273,12 @@ namespace {
}
void subcase_start(const SubcaseSignature& subc) override {
- std::lock_guard<std::mutex> lock(mutex);
subcasesStack.push_back(subc);
++currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
void subcase_end() override {
- std::lock_guard<std::mutex> lock(mutex);
--currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
@@ -3206,7 +3287,7 @@ namespace {
if((!rb.m_failed && !opt.success) || tc->m_no_output)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
logTestStart();
@@ -3222,7 +3303,7 @@ namespace {
if(tc->m_no_output)
return;
- std::lock_guard<std::mutex> lock(mutex);
+ DOCTEST_LOCK_MUTEX(mutex)
logTestStart();
@@ -3333,18 +3414,42 @@ namespace {
std::vector<String>& res) {
String filtersString;
if(parseOption(argc, argv, pattern, &filtersString)) {
- // tokenize with "," as a separator
- // cppcheck-suppress strtokCalled
- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
- auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string
- while(pch != nullptr) {
- if(strlen(pch))
- res.push_back(pch);
- // uses the strtok() internal state to go to the next token
- // cppcheck-suppress strtokCalled
- pch = std::strtok(nullptr, ",");
+ // tokenize with "," as a separator, unless escaped with backslash
+ std::ostringstream s;
+ auto flush = [&s, &res]() {
+ auto string = s.str();
+ if(string.size() > 0) {
+ res.push_back(string.c_str());
+ }
+ s.str("");
+ };
+
+ bool seenBackslash = false;
+ const char* current = filtersString.c_str();
+ const char* end = current + strlen(current);
+ while(current != end) {
+ char character = *current++;
+ if(seenBackslash) {
+ seenBackslash = false;
+ if(character == ',' || character == '\\') {
+ s.put(character);
+ continue;
+ }
+ s.put('\\');
+ }
+ if(character == '\\') {
+ seenBackslash = true;
+ } else if(character == ',') {
+ flush();
+ } else {
+ s.put(character);
+ }
+ }
+
+ if(seenBackslash) {
+ s.put('\\');
}
- DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ flush();
return true;
}
return false;
@@ -3363,30 +3468,30 @@ namespace {
if(!parseOption(argc, argv, pattern, &parsedValue))
return false;
- if(type == 0) {
+ if(type) {
+ // integer
+ // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse...
+ int theInt = std::atoi(parsedValue.c_str());
+ if (theInt != 0) {
+ res = theInt; //!OCLINT parameter reassignment
+ return true;
+ }
+ } else {
// boolean
- const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1
- const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1
+ const char positive[][5] = { "1", "true", "on", "yes" }; // 5 - strlen("true") + 1
+ const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1
// if the value matches any of the positive/negative possibilities
- for(unsigned i = 0; i < 4; i++) {
- if(parsedValue.compare(positive[i], true) == 0) {
+ for (unsigned i = 0; i < 4; i++) {
+ if (parsedValue.compare(positive[i], true) == 0) {
res = 1; //!OCLINT parameter reassignment
return true;
}
- if(parsedValue.compare(negative[i], true) == 0) {
+ if (parsedValue.compare(negative[i], true) == 0) {
res = 0; //!OCLINT parameter reassignment
return true;
}
}
- } else {
- // integer
- // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse...
- int theInt = std::atoi(parsedValue.c_str()); // NOLINT
- if(theInt != 0) {
- res = theInt; //!OCLINT parameter reassignment
- return true;
- }
}
return false;
}
@@ -3477,9 +3582,12 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false);
@@ -3543,10 +3651,14 @@ void Context::clearFilters() {
curr.clear();
}
-// allows the user to override procedurally the int/bool options from the command line
+// allows the user to override procedurally the bool options from the command line
+void Context::setOption(const char* option, bool value) {
+ setOption(option, value ? "true" : "false");
+}
+
+// allows the user to override procedurally the int options from the command line
void Context::setOption(const char* option, int value) {
setOption(option, toString(value).c_str());
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
// allows the user to override procedurally the string options from the command line
@@ -3563,6 +3675,31 @@ void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; }
void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; }
+void Context::setCout(std::ostream* out) { p->cout = out; }
+
+static class DiscardOStream : public std::ostream
+{
+private:
+ class : public std::streambuf
+ {
+ private:
+ // allowing some buffering decreases the amount of calls to overflow
+ char buf[1024];
+
+ protected:
+ std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; }
+
+ int_type overflow(int_type ch) override {
+ setp(std::begin(buf), std::end(buf));
+ return traits_type::not_eof(ch);
+ }
+ } discardBuf;
+
+public:
+ DiscardOStream()
+ : std::ostream(&discardBuf) {}
+} discardOut;
+
// the main function that does all the filtering and test running
int Context::run() {
using namespace detail;
@@ -3576,15 +3713,22 @@ int Context::run() {
g_no_colors = p->no_colors;
p->resetRunData();
- // stdout by default
- p->cout = &std::cout;
- p->cerr = &std::cerr;
-
- // or to a file if specified
std::fstream fstr;
- if(p->out.size()) {
- fstr.open(p->out.c_str(), std::fstream::out);
- p->cout = &fstr;
+ if(p->cout == nullptr) {
+ if(p->quiet) {
+ p->cout = &discardOut;
+ } else if(p->out.size()) {
+ // to a file if specified
+ fstr.open(p->out.c_str(), std::fstream::out);
+ p->cout = &fstr;
+ } else {
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ // stdout by default
+ p->cout = &std::cout;
+#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ return EXIT_FAILURE;
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+ }
}
FatalConditionHandler::allocateAltStackMem();
@@ -3656,7 +3800,7 @@ int Context::run() {
// random_shuffle implementation
const auto first = &testArray[0];
for(size_t i = testArray.size() - 1; i > 0; --i) {
- int idxToSwap = std::rand() % (i + 1); // NOLINT
+ int idxToSwap = std::rand() % (i + 1);
const auto temp = first[i];
@@ -3743,19 +3887,20 @@ int Context::run() {
p->numAssertsFailedCurrentTest_atomic = 0;
p->numAssertsCurrentTest_atomic = 0;
- p->subcasesPassed.clear();
+ p->fullyTraversedSubcases.clear();
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
p->timer.start();
-
+
bool run_test = true;
do {
// reset some of the fields for subcases (except for the set of fully passed ones)
- p->should_reenter = false;
- p->subcasesCurrentMaxLevel = 0;
- p->subcasesStack.clear();
+ p->reachedLeaf = false;
+ // May not be empty if previous subcase exited via exception.
+ p->subcaseStack.clear();
+ p->currentSubcaseDepth = 0;
p->shouldLogCurrentException = true;
@@ -3788,10 +3933,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
run_test = false;
p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
}
-
- if(p->should_reenter && run_test)
+
+ if(!p->nextSubcaseStack.empty() && run_test)
DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
- if(!p->should_reenter)
+ if(p->nextSubcaseStack.empty())
run_test = false;
} while(run_test);
@@ -3817,17 +3962,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata);
}
- // see these issues on the reasoning for this:
- // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903
- // - https://github.com/onqtam/doctest/issues/126
- auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE
- { std::cout << std::string(); };
- DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS();
-
return cleanup_and_return();
}
-IReporter::~IReporter() = default;
+DOCTEST_DEFINE_INTERFACE(IReporter)
int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); }
const IContextScope* const* IReporter::get_active_contexts() {
@@ -3862,5 +4000,17 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
+
#endif // DOCTEST_LIBRARY_IMPLEMENTATION
#endif // DOCTEST_CONFIG_IMPLEMENT
+
+#ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#undef WIN32_LEAN_AND_MEAN
+#undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+#endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN
+
+#ifdef DOCTEST_UNDEF_NOMINMAX
+#undef NOMINMAX
+#undef DOCTEST_UNDEF_NOMINMAX
+#endif // DOCTEST_UNDEF_NOMINMAX
diff --git a/contrib/doctest/doctest/parts/doctest_fwd.h b/contrib/doctest/doctest/parts/doctest_fwd.h
index e83b0ca7e..3e08e81b9 100644
--- a/contrib/doctest/doctest/parts/doctest_fwd.h
+++ b/contrib/doctest/doctest/parts/doctest_fwd.h
@@ -1,14 +1,14 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2021 Viktor Kirilov
+// Copyright (c) 2016-2023 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT
//
// The documentation can be found at the library's page:
-// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
+// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
//
// =================================================================================================
// =================================================================================================
@@ -45,8 +45,16 @@
#define DOCTEST_VERSION_MAJOR 2
#define DOCTEST_VERSION_MINOR 4
-#define DOCTEST_VERSION_PATCH 6
-#define DOCTEST_VERSION_STR "2.4.6"
+#define DOCTEST_VERSION_PATCH 11
+
+// util we need here
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+
+#define DOCTEST_VERSION_STR \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH)
#define DOCTEST_VERSION \
(DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
@@ -57,6 +65,12 @@
// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect
+#ifdef _MSC_VER
+#define DOCTEST_CPLUSPLUS _MSVC_LANG
+#else
+#define DOCTEST_CPLUSPLUS __cplusplus
+#endif
+
#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH))
// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
@@ -68,12 +82,15 @@
DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
#endif // MSVC
#endif // MSVC
-#if defined(__clang__) && defined(__clang_minor__)
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
!defined(__INTEL_COMPILER)
#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
#ifndef DOCTEST_MSVC
#define DOCTEST_MSVC 0
@@ -84,12 +101,15 @@
#ifndef DOCTEST_GCC
#define DOCTEST_GCC 0
#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
// =================================================================================================
// == COMPILER WARNINGS HELPERS ====================================================================
// =================================================================================================
-#if DOCTEST_CLANG
+#if DOCTEST_CLANG && !DOCTEST_ICC
#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
@@ -134,85 +154,94 @@
// == COMPILER WARNINGS ============================================================================
// =================================================================================================
+// both the header and the implementation suppress all of these,
+// so it only makes sense to aggregate them like so
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
+ \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
+ \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ /* these 4 also disabled globally via cmake: */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
+ /* common ones */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
+ /* static analysis */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */
+
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas")
-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing")
DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations")
DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast")
-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning
-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration
-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression
-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated
-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant
-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding
-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted
DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted
-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe
-// static analysis
-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept'
-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable
-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ...
-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr...
-DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
-
-// 4548 - expression before comma has no effect; expected expression with side - effect
-// 4265 - class has virtual functions, but destructor is not virtual
-// 4986 - exception specification does not match previous declaration
-// 4350 - behavior change: 'member1' called instead of 'member2'
-// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
-// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch
-// 4774 - format string expected in argument 'x' is not a string literal
-// 4820 - padding in structs
-
-// only 4 should be disabled globally:
-// - 4514 # unreferenced inline function has been removed
-// - 4571 # SEH related
-// - 4710 # function not inlined
-// - 4711 # function 'x' selected for automatic inline expansion
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
- DOCTEST_MSVC_SUPPRESS_WARNING(4548) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4265) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4986) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4350) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4668) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4365) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4774) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4820) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4625) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4626) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5027) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5026) \
- DOCTEST_MSVC_SUPPRESS_WARNING(4623) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5039) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5045) \
- DOCTEST_MSVC_SUPPRESS_WARNING(5105)
+ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -225,6 +254,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
// MSVC version table:
// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
+// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022)
// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019)
// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017)
// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
@@ -234,6 +264,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+// Universal Windows Platform support
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_WINDOWS_SEH
+#endif // WINAPI_FAMILY
#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
#define DOCTEST_CONFIG_WINDOWS_SEH
#endif // MSVC
@@ -242,7 +276,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
- !defined(__EMSCRIPTEN__)
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
#define DOCTEST_CONFIG_POSIX_SIGNALS
#endif // _WIN32
#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
@@ -250,7 +284,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
-#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
+ || defined(__wasi__)
#define DOCTEST_CONFIG_NO_EXCEPTIONS
#endif // no exceptions
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -265,6 +300,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
#define DOCTEST_CONFIG_IMPLEMENT
#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@@ -292,6 +331,16 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_INTERFACE
#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+// needed for extern template instantiations
+// see https://github.com/fmtlib/fmt/issues/2228
+#if DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL
+#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE
+#else // DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE
+#define DOCTEST_INTERFACE_DEF
+#endif // DOCTEST_MSVC
+
#define DOCTEST_EMPTY
#if DOCTEST_MSVC
@@ -308,18 +357,61 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
#endif
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
#ifndef DOCTEST_NORETURN
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NORETURN
+#else // DOCTEST_MSVC
#define DOCTEST_NORETURN [[noreturn]]
+#endif // DOCTEST_MSVC
#endif // DOCTEST_NORETURN
#ifndef DOCTEST_NOEXCEPT
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NOEXCEPT
+#else // DOCTEST_MSVC
#define DOCTEST_NOEXCEPT noexcept
+#endif // DOCTEST_MSVC
#endif // DOCTEST_NOEXCEPT
+#ifndef DOCTEST_CONSTEXPR
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_CONSTEXPR const
+#define DOCTEST_CONSTEXPR_FUNC inline
+#else // DOCTEST_MSVC
+#define DOCTEST_CONSTEXPR constexpr
+#define DOCTEST_CONSTEXPR_FUNC constexpr
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_CONSTEXPR
+
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
// =================================================================================================
+#define DOCTEST_DECLARE_INTERFACE(name) \
+ virtual ~name(); \
+ name() = default; \
+ name(const name&) = delete; \
+ name(name&&) = delete; \
+ name& operator=(const name&) = delete; \
+ name& operator=(name&&) = delete;
+
+#define DOCTEST_DEFINE_INTERFACE(name) \
+ name::~name() = default;
+
// internal macros for string concatenation and anonymous variable name generation
#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
@@ -329,8 +421,6 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
#endif // __COUNTER__
-#define DOCTEST_TOSTR(x) #x
-
#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
#define DOCTEST_REF_WRAP(x) x&
#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
@@ -344,31 +434,39 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_PLATFORM_IPHONE
#elif defined(_WIN32)
#define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
#else // DOCTEST_PLATFORM
#define DOCTEST_PLATFORM_LINUX
#endif // DOCTEST_PLATFORM
-#define DOCTEST_GLOBAL_NO_WARNINGS(var) \
- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
- DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \
- static const int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
-#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP
+namespace doctest { namespace detail {
+ static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; }
+}}
+
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
#ifndef DOCTEST_BREAK_INTO_DEBUGGER
// should probably take a look at https://github.com/scottt/debugbreak
#ifdef DOCTEST_PLATFORM_LINUX
#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
// Break at the location of the failing check if possible
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
#else
#include <signal.h>
#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
#endif
#elif defined(DOCTEST_PLATFORM_MAC)
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#elif defined(__ppc__) || defined(__ppc64__)
+// https://www.cocoawithlove.com/2008/03/break-into-debugger.html
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler)
#else
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler)
#endif
#elif DOCTEST_MSVC
#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
@@ -384,54 +482,67 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
// this is kept here for backwards compatibility since the config option was changed
#ifdef DOCTEST_CONFIG_USE_IOSFWD
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
#endif // DOCTEST_CONFIG_USE_IOSFWD
+// for clang - always include ciso646 (which drags some std stuff) because
+// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in
+// which case we don't want to forward declare stuff from std - for reference:
+// https://github.com/doctest/doctest/issues/126
+// https://github.com/doctest/doctest/issues/356
+#if DOCTEST_CLANG
+#include <ciso646>
+#endif // clang
+
+#ifdef _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // _LIBCPP_VERSION
+
#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
-#include <iosfwd>
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <cstddef>
#include <ostream>
+#include <istream>
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#else // DOCTEST_CONFIG_USE_STD_HEADERS
-#if DOCTEST_CLANG
-// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier)
-#include <ciso646>
-#endif // clang
-
-#ifdef _LIBCPP_VERSION
-#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD
-#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD
-#else // _LIBCPP_VERSION
-#define DOCTEST_STD_NAMESPACE_BEGIN namespace std {
-#define DOCTEST_STD_NAMESPACE_END }
-#endif // _LIBCPP_VERSION
-
// Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
-DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp)
-typedef decltype(nullptr) nullptr_t;
+namespace std { // NOLINT(cert-dcl58-cpp)
+typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using)
+typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using)
template <class charT>
struct char_traits;
template <>
struct char_traits<char>;
template <class charT, class traits>
-class basic_ostream;
-typedef basic_ostream<char, char_traits<char>> ostream;
+class basic_ostream; // NOLINT(fuchsia-virtual-inheritance)
+typedef basic_ostream<char, char_traits<char>> ostream; // NOLINT(modernize-use-using)
+template<class traits>
+// NOLINTNEXTLINE
+basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>&, const char*);
+template <class charT, class traits>
+class basic_istream;
+typedef basic_istream<char, char_traits<char>> istream; // NOLINT(modernize-use-using)
template <class... Types>
class tuple;
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
-template <class _Ty>
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+template <class Ty>
class allocator;
-template <class _Elem, class _Traits, class _Alloc>
+template <class Elem, class Traits, class Alloc>
class basic_string;
using string = basic_string<char, char_traits<char>, allocator<char>>;
#endif // VS 2019
-DOCTEST_STD_NAMESPACE_END
+} // namespace std
DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -443,8 +554,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
namespace doctest {
+using std::size_t;
+
DOCTEST_INTERFACE extern bool is_running_in_test;
+#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE
+#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned
+#endif
+
// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
@@ -457,7 +574,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// TODO:
// - optimizations - like not deleting memory unnecessarily in operator= and etc.
// - resize/reserve/clear
-// - substr
// - replace
// - back/front
// - iterator stuff
@@ -467,63 +583,84 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// - relational operators as free functions - taking const char* as one of the params
class DOCTEST_INTERFACE String
{
- static const unsigned len = 24; //!OCLINT avoid private static members
- static const unsigned last = len - 1; //!OCLINT avoid private static members
+public:
+ using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE;
+
+private:
+ static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members
+ static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members
struct view // len should be more than sizeof(view) - because of the final byte for flags
{
char* ptr;
- unsigned size;
- unsigned capacity;
+ size_type size;
+ size_type capacity;
};
union
{
- char buf[len];
+ char buf[len]; // NOLINT(*-avoid-c-arrays)
view data;
};
- bool isOnStack() const { return (buf[last] & 128) == 0; }
- void setOnHeap();
- void setLast(unsigned in = last);
+ char* allocate(size_type sz);
+
+ bool isOnStack() const noexcept { return (buf[last] & 128) == 0; }
+ void setOnHeap() noexcept;
+ void setLast(size_type in = last) noexcept;
+ void setSize(size_type sz) noexcept;
void copy(const String& other);
public:
- String();
+ static DOCTEST_CONSTEXPR size_type npos = static_cast<size_type>(-1);
+
+ String() noexcept;
~String();
// cppcheck-suppress noExplicitConstructor
String(const char* in);
- String(const char* in, unsigned in_size);
+ String(const char* in, size_type in_size);
+
+ String(std::istream& in, size_type in_size);
String(const String& other);
String& operator=(const String& other);
String& operator+=(const String& other);
- String operator+(const String& other) const;
- String(String&& other);
- String& operator=(String&& other);
+ String(String&& other) noexcept;
+ String& operator=(String&& other) noexcept;
- char operator[](unsigned i) const;
- char& operator[](unsigned i);
+ char operator[](size_type i) const;
+ char& operator[](size_type i);
// the only functions I'm willing to leave in the interface - available for inlining
const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT
char* c_str() {
- if(isOnStack())
+ if (isOnStack()) {
return reinterpret_cast<char*>(buf);
+ }
return data.ptr;
}
- unsigned size() const;
- unsigned capacity() const;
+ size_type size() const;
+ size_type capacity() const;
+
+ String substr(size_type pos, size_type cnt = npos) &&;
+ String substr(size_type pos, size_type cnt = npos) const &;
+
+ size_type find(char ch, size_type pos = 0) const;
+ size_type rfind(char ch, size_type pos = npos) const;
int compare(const char* other, bool no_case = false) const;
int compare(const String& other, bool no_case = false) const;
+
+friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
};
+DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
+
DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs);
@@ -531,7 +668,21 @@ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs);
-DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
+class DOCTEST_INTERFACE Contains {
+public:
+ explicit Contains(const String& string);
+
+ bool checkWith(const String& other) const;
+
+ String string;
+};
+
+DOCTEST_INTERFACE String toString(const Contains& in);
+
+DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs);
namespace Color {
enum Enum
@@ -604,7 +755,7 @@ namespace assertType {
DT_WARN_THROWS_WITH = is_throws_with | is_warn,
DT_CHECK_THROWS_WITH = is_throws_with | is_check,
DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
-
+
DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
@@ -685,9 +836,27 @@ struct DOCTEST_INTERFACE AssertData
String m_decomp;
// for specific exception-related asserts
- bool m_threw_as;
- const char* m_exception_type;
- const char* m_exception_string;
+ bool m_threw_as;
+ const char* m_exception_type;
+
+ class DOCTEST_INTERFACE StringContains {
+ private:
+ Contains content;
+ bool isContains;
+
+ public:
+ StringContains(const String& str) : content(str), isContains(false) { }
+ StringContains(Contains cntn) : content(static_cast<Contains&&>(cntn)), isContains(true) { }
+
+ bool check(const String& str) { return isContains ? (content == str) : (content.string == str); }
+
+ operator const String&() const { return content.string; }
+
+ const char* c_str() const { return content.string.c_str(); }
+ } m_exception_string;
+
+ AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string);
};
struct DOCTEST_INTERFACE MessageData
@@ -704,13 +873,13 @@ struct DOCTEST_INTERFACE SubcaseSignature
const char* m_file;
int m_line;
+ bool operator==(const SubcaseSignature& other) const;
bool operator<(const SubcaseSignature& other) const;
};
struct DOCTEST_INTERFACE IContextScope
{
- IContextScope();
- virtual ~IContextScope();
+ DOCTEST_DECLARE_INTERFACE(IContextScope)
virtual void stringify(std::ostream*) const = 0;
};
@@ -720,9 +889,8 @@ namespace detail {
struct ContextOptions //!OCLINT too many fields
{
- std::ostream* cout; // stdout stream - std::cout by default
- std::ostream* cerr; // stderr stream - std::cerr by default
- String binary_name; // the test binary name
+ std::ostream* cout = nullptr; // stdout stream
+ String binary_name; // the test binary name
const detail::TestCase* currentTest = nullptr;
@@ -741,9 +909,12 @@ struct ContextOptions //!OCLINT too many fields
bool case_sensitive; // if filtering should be case sensitive
bool exit; // if the program should be exited after the tests are ran/whatever
bool duration; // print the time duration of each test case
+ bool minimal; // minimal console output (only test failures)
+ bool quiet; // no console output
bool no_throw; // to skip exceptions-related assertion macros
bool no_exitcode; // if the framework should return 0 as the exitcode
bool no_run; // to not run the tests at all (can be done with an "*" exclude)
+ bool no_intro; // to not print the intro of the framework
bool no_version; // to not print the version of the framework
bool no_colors; // if output to the console should be colorized
bool force_colors; // forces the use of colors even when a tty cannot be detected
@@ -765,150 +936,189 @@ struct ContextOptions //!OCLINT too many fields
};
namespace detail {
- template <bool CONDITION, typename TYPE = void>
- struct enable_if
- {};
+ namespace types {
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ using namespace std;
+#else
+ template <bool COND, typename T = void>
+ struct enable_if { };
+
+ template <typename T>
+ struct enable_if<true, T> { using type = T; };
- template <typename TYPE>
- struct enable_if<true, TYPE>
- { typedef TYPE type; };
+ struct true_type { static DOCTEST_CONSTEXPR bool value = true; };
+ struct false_type { static DOCTEST_CONSTEXPR bool value = false; };
- // clang-format off
- template<class T> struct remove_reference { typedef T type; };
- template<class T> struct remove_reference<T&> { typedef T type; };
- template<class T> struct remove_reference<T&&> { typedef T type; };
+ template <typename T> struct remove_reference { using type = T; };
+ template <typename T> struct remove_reference<T&> { using type = T; };
+ template <typename T> struct remove_reference<T&&> { using type = T; };
- template<typename T, typename U = T&&> U declval(int);
+ template <typename T> struct is_rvalue_reference : false_type { };
+ template <typename T> struct is_rvalue_reference<T&&> : true_type { };
- template<typename T> T declval(long);
+ template<typename T> struct remove_const { using type = T; };
+ template <typename T> struct remove_const<const T> { using type = T; };
- template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ;
+ // Compiler intrinsics
+ template <typename T> struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); };
+ template <typename T> struct underlying_type { using type = __underlying_type(T); };
- template<class T> struct is_lvalue_reference { const static bool value=false; };
- template<class T> struct is_lvalue_reference<T&> { const static bool value=true; };
+ template <typename T> struct is_pointer : false_type { };
+ template <typename T> struct is_pointer<T*> : true_type { };
+
+ template <typename T> struct is_array : false_type { };
+ // NOLINTNEXTLINE(*-avoid-c-arrays)
+ template <typename T, size_t SIZE> struct is_array<T[SIZE]> : true_type { };
+#endif
+ }
+
+ // <utility>
+ template <typename T>
+ T&& declval();
template <class T>
- inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT
- {
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type& t) DOCTEST_NOEXCEPT {
return static_cast<T&&>(t);
}
template <class T>
- inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT
- {
- static_assert(!is_lvalue_reference<T>::value,
- "Can not forward an rvalue as an lvalue.");
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type&& t) DOCTEST_NOEXCEPT {
return static_cast<T&&>(t);
}
- template<class T> struct remove_const { typedef T type; };
- template<class T> struct remove_const<const T> { typedef T type; };
-#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
- template<class T> struct is_enum : public std::is_enum<T> {};
- template<class T> struct underlying_type : public std::underlying_type<T> {};
-#else
- // Use compiler intrinsics
- template<class T> struct is_enum { constexpr static bool value = __is_enum(T); };
- template<class T> struct underlying_type { typedef __underlying_type(T) type; };
-#endif
- // clang-format on
+ template <typename T>
+ struct deferred_false : types::false_type { };
+
+// MSVS 2015 :(
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
+ template <typename T, typename = void>
+ struct has_global_insertion_operator : types::false_type { };
template <typename T>
- struct deferred_false
- // cppcheck-suppress unusedStructMember
- { static const bool value = false; };
-
- namespace has_insertion_operator_impl {
- std::ostream &os();
- template<class T>
- DOCTEST_REF_WRAP(T) val();
-
- template<class, class = void>
- struct check {
- static constexpr bool value = false;
- };
+ struct has_global_insertion_operator<T, decltype(::operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
- template<class T>
- struct check<T, decltype(os() << val<T>(), void())> {
- static constexpr bool value = true;
- };
- } // namespace has_insertion_operator_impl
+ template <typename T, typename = void>
+ struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator<T>::value; };
+
+ template <typename T, bool global>
+ struct insert_hack;
+
+ template <typename T>
+ struct insert_hack<T, true> {
+ static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); }
+ };
+
+ template <typename T>
+ struct insert_hack<T, false> {
+ static void insert(std::ostream& os, const T& t) { operator<<(os, t); }
+ };
- template<class T>
- using has_insertion_operator = has_insertion_operator_impl::check<const T>;
+ template <typename T>
+ using insert_hack_t = insert_hack<T, has_global_insertion_operator<T>::value>;
+#else
+ template <typename T, typename = void>
+ struct has_insertion_operator : types::false_type { };
+#endif
- DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num);
+ template <typename T>
+ struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
+
+ template <typename T>
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum<T>::value && !doctest::detail::has_insertion_operator<T>::value;
+ };
- DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream
- DOCTEST_INTERFACE String getTlsOssResult();
+ DOCTEST_INTERFACE std::ostream* tlssPush();
+ DOCTEST_INTERFACE String tlssPop();
template <bool C>
- struct StringMakerBase
- {
+ struct StringMakerBase {
template <typename T>
static String convert(const DOCTEST_REF_WRAP(T)) {
+#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES
+ static_assert(deferred_false<T>::value, "No stringification detected for type T. See string conversion manual");
+#endif
return "{?}";
}
};
- template <>
- struct StringMakerBase<true>
- {
- template <typename T>
- static String convert(const DOCTEST_REF_WRAP(T) in) {
- *getTlsOss() << in;
- return getTlsOssResult();
- }
- };
-
- DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size);
+ template <typename T>
+ struct filldata;
template <typename T>
- String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) {
- return rawMemoryToString(&object, sizeof(object));
+ void filloss(std::ostream* stream, const T& in) {
+ filldata<T>::fill(stream, in);
+ }
+
+ template <typename T, size_t N>
+ void filloss(std::ostream* stream, const T (&in)[N]) { // NOLINT(*-avoid-c-arrays)
+ // T[N], T(&)[N], T(&&)[N] have same behaviour.
+ // Hence remove reference.
+ filloss<typename types::remove_reference<decltype(in)>::type>(stream, in);
}
template <typename T>
- const char* type_to_string() {
- return "<>";
+ String toStream(const T& in) {
+ std::ostream* stream = tlssPush();
+ filloss(stream, in);
+ return tlssPop();
}
+
+ template <>
+ struct StringMakerBase<true> {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+ return toStream(in);
+ }
+ };
} // namespace detail
template <typename T>
-struct StringMaker : public detail::StringMakerBase<detail::has_insertion_operator<T>::value>
+struct StringMaker : public detail::StringMakerBase<
+ detail::has_insertion_operator<T>::value || detail::types::is_pointer<T>::value || detail::types::is_array<T>::value>
{};
-template <typename T>
-struct StringMaker<T*>
-{
- template <typename U>
- static String convert(U* p) {
- if(p)
- return detail::rawMemoryToString(p);
- return "NULL";
- }
-};
+#ifndef DOCTEST_STRINGIFY
+#ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY
+#define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__))
+#else
+#define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__)
+#endif
+#endif
-template <typename R, typename C>
-struct StringMaker<R C::*>
-{
- static String convert(R C::*p) {
- if(p)
- return detail::rawMemoryToString(p);
- return "NULL";
- }
-};
+template <typename T>
+String toString() {
+#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0
+ String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void)
+ String::size_type beginPos = ret.find('<');
+ return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(sizeof(">(void)")));
+#else
+ String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE]
+ String::size_type begin = ret.find('=') + 2;
+ return ret.substr(begin, ret.size() - begin - 1);
+#endif
+}
-template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true>
+template <typename T, typename detail::types::enable_if<!detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
return StringMaker<T>::convert(value);
}
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-DOCTEST_INTERFACE String toString(char* in);
DOCTEST_INTERFACE String toString(const char* in);
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+DOCTEST_INTERFACE String toString(const std::string& in);
+#endif // VS 2019
+
+DOCTEST_INTERFACE String toString(String in);
+
+DOCTEST_INTERFACE String toString(std::nullptr_t);
+
DOCTEST_INTERFACE String toString(bool in);
+
DOCTEST_INTERFACE String toString(float in);
DOCTEST_INTERFACE String toString(double in);
DOCTEST_INTERFACE String toString(double long in);
@@ -916,40 +1126,95 @@ DOCTEST_INTERFACE String toString(double long in);
DOCTEST_INTERFACE String toString(char in);
DOCTEST_INTERFACE String toString(char signed in);
DOCTEST_INTERFACE String toString(char unsigned in);
-DOCTEST_INTERFACE String toString(int short in);
-DOCTEST_INTERFACE String toString(int short unsigned in);
-DOCTEST_INTERFACE String toString(int in);
-DOCTEST_INTERFACE String toString(int unsigned in);
-DOCTEST_INTERFACE String toString(int long in);
-DOCTEST_INTERFACE String toString(int long unsigned in);
-DOCTEST_INTERFACE String toString(int long long in);
-DOCTEST_INTERFACE String toString(int long long unsigned in);
-DOCTEST_INTERFACE String toString(std::nullptr_t in);
-
-template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true>
+DOCTEST_INTERFACE String toString(short in);
+DOCTEST_INTERFACE String toString(short unsigned in);
+DOCTEST_INTERFACE String toString(signed in);
+DOCTEST_INTERFACE String toString(unsigned in);
+DOCTEST_INTERFACE String toString(long in);
+DOCTEST_INTERFACE String toString(long unsigned in);
+DOCTEST_INTERFACE String toString(long long in);
+DOCTEST_INTERFACE String toString(long long unsigned in);
+
+template <typename T, typename detail::types::enable_if<detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
- typedef typename detail::underlying_type<T>::type UT;
- return toString(static_cast<UT>(value));
+ using UT = typename detail::types::underlying_type<T>::type;
+ return (DOCTEST_STRINGIFY(static_cast<UT>(value)));
}
-#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
-DOCTEST_INTERFACE String toString(const std::string& in);
-#endif // VS 2019
+namespace detail {
+ template <typename T>
+ struct filldata
+ {
+ static void fill(std::ostream* stream, const T& in) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ insert_hack_t<T>::insert(*stream, in);
+#else
+ operator<<(*stream, in);
+#endif
+ }
+ };
+
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+// NOLINTBEGIN(*-avoid-c-arrays)
+ template <typename T, size_t N>
+ struct filldata<T[N]> {
+ static void fill(std::ostream* stream, const T(&in)[N]) {
+ *stream << "[";
+ for (size_t i = 0; i < N; i++) {
+ if (i != 0) { *stream << ", "; }
+ *stream << (DOCTEST_STRINGIFY(in[i]));
+ }
+ *stream << "]";
+ }
+ };
+// NOLINTEND(*-avoid-c-arrays)
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
-class DOCTEST_INTERFACE Approx
+ // Specialized since we don't want the terminating null byte!
+// NOLINTBEGIN(*-avoid-c-arrays)
+ template <size_t N>
+ struct filldata<const char[N]> {
+ static void fill(std::ostream* stream, const char (&in)[N]) {
+ *stream << String(in, in[N - 1] ? N : N - 1);
+ } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+ };
+// NOLINTEND(*-avoid-c-arrays)
+
+ template <>
+ struct filldata<const void*> {
+ static void fill(std::ostream* stream, const void* in);
+ };
+
+ template <typename T>
+ struct filldata<T*> {
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180)
+ static void fill(std::ostream* stream, const T* in) {
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast")
+ filldata<const void*>::fill(stream,
+#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)
+ reinterpret_cast<const void*>(in)
+#else
+ *reinterpret_cast<const void* const*>(&in)
+#endif
+ );
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ }
+ };
+}
+
+struct DOCTEST_INTERFACE Approx
{
-public:
- explicit Approx(double value);
+ Approx(double value);
Approx operator()(double value) const;
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
explicit Approx(const T& value,
- typename detail::enable_if<std::is_constructible<double, T>::value>::type* =
+ typename detail::types::enable_if<std::is_constructible<double, T>::value>::type* =
static_cast<T*>(nullptr)) {
- *this = Approx(static_cast<double>(value));
+ *this = static_cast<double>(value);
}
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
@@ -957,7 +1222,7 @@ public:
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
- typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
const T& newEpsilon) {
m_epsilon = static_cast<double>(newEpsilon);
return *this;
@@ -968,7 +1233,7 @@ public:
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
template <typename T>
- typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
const T& newScale) {
m_scale = static_cast<double>(newScale);
return *this;
@@ -989,30 +1254,27 @@ public:
DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs);
DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs);
- DOCTEST_INTERFACE friend String toString(const Approx& in);
-
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#define DOCTEST_APPROX_PREFIX \
- template <typename T> friend typename detail::enable_if<std::is_constructible<double, T>::value, bool>::type
+ template <typename T> friend typename std::enable_if<std::is_constructible<double, T>::value, bool>::type
- DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); }
+ DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast<double>(lhs), rhs); }
DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
- DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; }
- DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; }
- DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) && lhs != rhs; }
#undef DOCTEST_APPROX_PREFIX
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
// clang-format on
-private:
double m_epsilon;
double m_scale;
double m_value;
@@ -1022,18 +1284,35 @@ DOCTEST_INTERFACE String toString(const Approx& in);
DOCTEST_INTERFACE const ContextOptions* getContextOptions();
-#if !defined(DOCTEST_CONFIG_DISABLE)
+template <typename F>
+struct DOCTEST_INTERFACE_DECL IsNaN
+{
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN<F> operator!() const { return { value, !flipped }; }
+ operator bool() const;
+};
+#ifndef __MINGW32__
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<float>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<double>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<long double>;
+#endif
+DOCTEST_INTERFACE String toString(IsNaN<float> in);
+DOCTEST_INTERFACE String toString(IsNaN<double> in);
+DOCTEST_INTERFACE String toString(IsNaN<double long> in);
+
+#ifndef DOCTEST_CONFIG_DISABLE
namespace detail {
// clang-format off
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- template<class T> struct decay_array { typedef T type; };
- template<class T, unsigned N> struct decay_array<T[N]> { typedef T* type; };
- template<class T> struct decay_array<T[]> { typedef T* type; };
+ template<class T> struct decay_array { using type = T; };
+ template<class T, unsigned N> struct decay_array<T[N]> { using type = T*; };
+ template<class T> struct decay_array<T[]> { using type = T*; };
- template<class T> struct not_char_pointer { enum { value = 1 }; };
- template<> struct not_char_pointer<char*> { enum { value = 0 }; };
- template<> struct not_char_pointer<const char*> { enum { value = 0 }; };
+ template<class T> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; };
+ template<> struct not_char_pointer<char*> { static DOCTEST_CONSTEXPR int value = 0; };
+ template<> struct not_char_pointer<const char*> { static DOCTEST_CONSTEXPR int value = 0; };
template<class T> struct can_use_op : public not_char_pointer<typename decay_array<T>::type> {};
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
@@ -1056,16 +1335,22 @@ namespace detail {
bool m_entered = false;
Subcase(const String& name, const char* file, int line);
+ Subcase(const Subcase&) = delete;
+ Subcase(Subcase&&) = delete;
+ Subcase& operator=(const Subcase&) = delete;
+ Subcase& operator=(Subcase&&) = delete;
~Subcase();
operator bool() const;
+
+ private:
+ bool checkFilters();
};
template <typename L, typename R>
String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
const DOCTEST_REF_WRAP(R) rhs) {
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
- return toString(lhs) + op + toString(rhs);
+ return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs));
}
#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
@@ -1076,12 +1361,16 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
// If not it doesn't find the operator or if the operator at global scope is defined after
// this template, the template won't be instantiated due to SFINAE. Once the template is not
// instantiated it can look for global operator using normal conversions.
-#define SFINAE_OP(ret,op) decltype(doctest::detail::declval<L>() op doctest::detail::declval<R>(),static_cast<ret>(0))
+#ifdef __NVCC__
+#define SFINAE_OP(ret,op) ret
+#else
+#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{})
+#endif
#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
template <typename R> \
- DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
- bool res = op_macro(doctest::detail::forward<L>(lhs), doctest::detail::forward<R>(rhs)); \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward<const L>(lhs), doctest::detail::forward<R>(rhs)); \
if(m_at & assertType::is_false) \
res = !res; \
if(!res || doctest::getContextOptions()->success) \
@@ -1100,11 +1389,12 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
return *this; \
}
- struct DOCTEST_INTERFACE Result
+ struct DOCTEST_INTERFACE Result // NOLINT(*-member-init)
{
bool m_passed;
String m_decomp;
+ Result() = default; // TODO: Why do we need this? (To remove NOLINT)
Result(bool passed, const String& decomposition = String());
// forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
@@ -1161,8 +1451,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#define DOCTEST_COMPARISON_RETURN_TYPE bool
#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
-#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
- // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
@@ -1210,26 +1499,26 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
assertType::Enum m_at;
explicit Expression_lhs(L&& in, assertType::Enum at)
- : lhs(doctest::detail::forward<L>(in))
+ : lhs(static_cast<L&&>(in))
, m_at(at) {}
DOCTEST_NOINLINE operator Result() {
-// this is needed only foc MSVC 2015:
-// https://ci.appveyor.com/project/onqtam/doctest/builds/38181202
+// this is needed only for MSVC 2015
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
bool res = static_cast<bool>(lhs);
DOCTEST_MSVC_SUPPRESS_WARNING_POP
- if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ if(m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
res = !res;
+ }
- if(!res || getContextOptions()->success)
- return Result(res, toString(lhs));
- return Result(res);
+ if(!res || getContextOptions()->success) {
+ return { res, (DOCTEST_STRINGIFY(lhs)) };
+ }
+ return { res };
}
- /* This is required for user-defined conversions from Expression_lhs to L */
- //operator L() const { return lhs; }
- operator L() const { return lhs; }
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ operator L() const { return lhs; }
// clang-format off
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
@@ -1286,22 +1575,27 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// https://github.com/catchorg/Catch2/issues/870
// https://github.com/catchorg/Catch2/issues/565
template <typename L>
- Expression_lhs<L> operator<<(L &&operand) {
- return Expression_lhs<L>(doctest::detail::forward<L>(operand), m_at);
+ Expression_lhs<L> operator<<(L&& operand) {
+ return Expression_lhs<L>(static_cast<L&&>(operand), m_at);
+ }
+
+ template <typename L,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<L>::value,void >::type* = nullptr>
+ Expression_lhs<const L&> operator<<(const L &operand) {
+ return Expression_lhs<const L&>(operand, m_at);
}
};
struct DOCTEST_INTERFACE TestSuite
{
- const char* m_test_suite;
- const char* m_description;
- bool m_skip;
- bool m_no_breaks;
- bool m_no_output;
- bool m_may_fail;
- bool m_should_fail;
- int m_expected_failures;
- double m_timeout;
+ const char* m_test_suite = nullptr;
+ const char* m_description = nullptr;
+ bool m_skip = false;
+ bool m_no_breaks = false;
+ bool m_no_output = false;
+ bool m_may_fail = false;
+ bool m_should_fail = false;
+ int m_expected_failures = 0;
+ double m_timeout = 0;
TestSuite& operator*(const char* in);
@@ -1312,25 +1606,28 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
}
};
- typedef void (*funcType)();
+ using funcType = void (*)();
struct DOCTEST_INTERFACE TestCase : public TestCaseData
{
funcType m_test; // a function pointer to the test case
- const char* m_type; // for templated test cases - gets appended to the real name
+ String m_type; // for templated test cases - gets appended to the real name
int m_template_id; // an ID used to distinguish between the different versions of a templated test case
String m_full_name; // contains the name (only for templated test cases!) + the template type
TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
- const char* type = "", int template_id = -1);
+ const String& type = String(), int template_id = -1);
TestCase(const TestCase& other);
+ TestCase(TestCase&&) = delete;
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
TestCase& operator=(const TestCase& other);
DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ TestCase& operator=(TestCase&&) = delete;
+
TestCase& operator*(const char* in);
template <typename T>
@@ -1340,6 +1637,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
}
bool operator<(const TestCase& other) const;
+
+ ~TestCase() = default;
};
// forward declarations of functions used by the macros
@@ -1379,27 +1678,36 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
struct DOCTEST_INTERFACE ResultBuilder : public AssertData
{
ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
- const char* exception_type = "", const char* exception_string = "");
+ const char* exception_type = "", const String& exception_string = "");
+
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string);
void setResult(const Result& res);
template <int comparison, typename L, typename R>
- DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs,
const DOCTEST_REF_WRAP(R) rhs) {
m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
- if(m_failed || getContextOptions()->success)
+ if (m_failed || getContextOptions()->success) {
m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
+ }
+ return !m_failed;
}
template <typename L>
- DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) {
+ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) {
m_failed = !val;
- if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
m_failed = !m_failed;
+ }
- if(m_failed || getContextOptions()->success)
- m_decomp = toString(val);
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = (DOCTEST_STRINGIFY(val));
+ }
+
+ return !m_failed;
}
void translateException();
@@ -1419,8 +1727,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad);
- DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line,
- const char* expr, Result result);
+ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const Result& result);
#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \
do { \
@@ -1435,7 +1743,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
if(checkIfShouldThrow(at)) \
throwException(); \
} \
- return; \
+ return !failed; \
} \
} while(false)
@@ -1450,7 +1758,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
throwException()
template <int comparison, typename L, typename R>
- DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line,
+ DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line,
const char* expr, const DOCTEST_REF_WRAP(L) lhs,
const DOCTEST_REF_WRAP(R) rhs) {
bool failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
@@ -1461,10 +1769,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// ###################################################################################
DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ return !failed;
}
template <typename L>
- DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line,
+ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line,
const char* expr, const DOCTEST_REF_WRAP(L) val) {
bool failed = !val;
@@ -1475,14 +1784,14 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
// IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
// THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
// ###################################################################################
- DOCTEST_ASSERT_OUT_OF_TESTS(toString(val));
- DOCTEST_ASSERT_IN_TESTS(toString(val));
+ DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val)));
+ DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val)));
+ return !failed;
}
struct DOCTEST_INTERFACE IExceptionTranslator
{
- IExceptionTranslator();
- virtual ~IExceptionTranslator();
+ DOCTEST_DECLARE_INTERFACE(IExceptionTranslator)
virtual bool translate(String&) const = 0;
};
@@ -1498,7 +1807,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
try {
throw; // lgtm [cpp/rethrow-no-exception]
// cppcheck-suppress catchExceptionByValue
- } catch(T ex) { // NOLINT
+ } catch(const T& ex) {
res = m_translateFunction(ex); //!OCLINT parameter reassignment
return true;
} catch(...) {} //!OCLINT - empty catch statement
@@ -1513,95 +1822,70 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et);
- template <bool C>
- struct StringStreamBase
- {
- template <typename T>
- static void convert(std::ostream* s, const T& in) {
- *s << toString(in);
- }
-
- // always treat char* as a string in this context - no matter
- // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined
- static void convert(std::ostream* s, const char* in) { *s << String(in); }
- };
-
- template <>
- struct StringStreamBase<true>
- {
- template <typename T>
- static void convert(std::ostream* s, const T& in) {
- *s << in;
- }
- };
+ // ContextScope base class used to allow implementing methods of ContextScope
+ // that don't depend on the template parameter in doctest.cpp.
+ struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
+ ContextScopeBase(const ContextScopeBase&) = delete;
- template <typename T>
- struct StringStream : public StringStreamBase<has_insertion_operator<T>::value>
- {};
+ ContextScopeBase& operator=(const ContextScopeBase&) = delete;
+ ContextScopeBase& operator=(ContextScopeBase&&) = delete;
- template <typename T>
- void toStream(std::ostream* s, const T& value) {
- StringStream<T>::convert(s, value);
- }
+ ~ContextScopeBase() override = default;
-#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- DOCTEST_INTERFACE void toStream(std::ostream* s, char* in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in);
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
- DOCTEST_INTERFACE void toStream(std::ostream* s, bool in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, float in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, double in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, double long in);
-
- DOCTEST_INTERFACE void toStream(std::ostream* s, char in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int short in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in);
- DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in);
-
- // ContextScope base class used to allow implementing methods of ContextScope
- // that don't depend on the template parameter in doctest.cpp.
- class DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
protected:
ContextScopeBase();
+ ContextScopeBase(ContextScopeBase&& other) noexcept;
void destroy();
+ bool need_to_destroy{true};
};
template <typename L> class ContextScope : public ContextScopeBase
{
- const L lambda_;
+ L lambda_;
public:
explicit ContextScope(const L &lambda) : lambda_(lambda) {}
+ explicit ContextScope(L&& lambda) : lambda_(static_cast<L&&>(lambda)) { }
- ContextScope(ContextScope &&other) : lambda_(other.lambda_) {}
+ ContextScope(const ContextScope&) = delete;
+ ContextScope(ContextScope&&) noexcept = default;
+
+ ContextScope& operator=(const ContextScope&) = delete;
+ ContextScope& operator=(ContextScope&&) = delete;
void stringify(std::ostream* s) const override { lambda_(s); }
- ~ContextScope() override { destroy(); }
+ ~ContextScope() override {
+ if (need_to_destroy) {
+ destroy();
+ }
+ }
};
struct DOCTEST_INTERFACE MessageBuilder : public MessageData
{
std::ostream* m_stream;
+ bool logged = false;
MessageBuilder(const char* file, int line, assertType::Enum severity);
- MessageBuilder() = delete;
+
+ MessageBuilder(const MessageBuilder&) = delete;
+ MessageBuilder(MessageBuilder&&) = delete;
+
+ MessageBuilder& operator=(const MessageBuilder&) = delete;
+ MessageBuilder& operator=(MessageBuilder&&) = delete;
+
~MessageBuilder();
// the preferred way of chaining parameters for stringification
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
template <typename T>
MessageBuilder& operator,(const T& in) {
- toStream(m_stream, in);
+ *m_stream << (DOCTEST_STRINGIFY(in));
return *this;
}
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
// kept here just for backwards-compatibility - the comma operator should be preferred now
template <typename T>
@@ -1617,7 +1901,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
bool log();
void react();
};
-
+
template <typename L>
ContextScope<L> MakeContextScope(const L &lambda) {
return ContextScope<L>(lambda);
@@ -1670,7 +1954,7 @@ int registerExceptionTranslator(String (*)(T)) {
#endif // DOCTEST_CONFIG_DISABLE
namespace detail {
- typedef void (*assert_handler)(const AssertData&);
+ using assert_handler = void (*)(const AssertData&);
struct ContextState;
} // namespace detail
@@ -1683,12 +1967,19 @@ class DOCTEST_INTERFACE Context
public:
explicit Context(int argc = 0, const char* const* argv = nullptr);
- ~Context();
+ Context(const Context&) = delete;
+ Context(Context&&) = delete;
+
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ ~Context(); // NOLINT(performance-trivially-destructible)
void applyCommandLine(int argc, const char* const* argv);
void addFilter(const char* filter, const char* value);
void clearFilters();
+ void setOption(const char* option, bool value);
void setOption(const char* option, int value);
void setOption(const char* option, const char* value);
@@ -1698,6 +1989,8 @@ public:
void setAssertHandler(detail::assert_handler ah);
+ void setCout(std::ostream* out);
+
int run();
};
@@ -1724,6 +2017,7 @@ struct DOCTEST_INTERFACE CurrentTestCaseStats
int numAssertsFailedCurrentTest;
double seconds;
int failure_flags; // use TestCaseFailureReason::Enum
+ bool testCaseSuccess;
};
struct DOCTEST_INTERFACE TestCaseException
@@ -1787,8 +2081,7 @@ struct DOCTEST_INTERFACE IReporter
// or isn't in the execution range (between first and last) (safe to cache a pointer to the input)
virtual void test_case_skipped(const TestCaseData&) = 0;
- // doctest will not be managing the lifetimes of reporters given to it but this would still be nice to have
- virtual ~IReporter();
+ DOCTEST_DECLARE_INTERFACE(IReporter)
// can obtain all currently active contexts and stringify them if one wishes to do so
static int get_num_active_contexts();
@@ -1800,7 +2093,7 @@ struct DOCTEST_INTERFACE IReporter
};
namespace detail {
- typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&);
+ using reporterCreatorFunc = IReporter* (*)(const ContextOptions&);
DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter);
@@ -1817,14 +2110,30 @@ int registerReporter(const char* name, int priority, bool isReporter) {
}
} // namespace doctest
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_EMPTY [] { return false; }()
+#else
+#define DOCTEST_FUNC_EMPTY (void)0
+#endif
+
// if registering is not disabled
-#if !defined(DOCTEST_CONFIG_DISABLE)
+#ifndef DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_SCOPE_BEGIN [&]
+#define DOCTEST_FUNC_SCOPE_END ()
+#define DOCTEST_FUNC_SCOPE_RET(v) return v
+#else
+#define DOCTEST_FUNC_SCOPE_BEGIN do
+#define DOCTEST_FUNC_SCOPE_END while(false)
+#define DOCTEST_FUNC_SCOPE_RET(v) (void)0
+#endif
// common code in asserts - for convenience
-#define DOCTEST_ASSERT_LOG_AND_REACT(b) \
- if(b.log()) \
- DOCTEST_BREAK_INTO_DEBUGGER(); \
- b.react()
+#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \
+ if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \
+ b.react(); \
+ DOCTEST_FUNC_SCOPE_RET(!b.m_failed)
#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#define DOCTEST_WRAP_IN_TRY(x) x;
@@ -1832,7 +2141,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_WRAP_IN_TRY(x) \
try { \
x; \
- } catch(...) { _DOCTEST_RB.translateException(); }
+ } catch(...) { DOCTEST_RB.translateException(); }
#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
@@ -1846,27 +2155,26 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// registers the test by initializing a dummy var with a function
#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \
- global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
+ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \
doctest::detail::regTest( \
doctest::detail::TestCase( \
f, __FILE__, __LINE__, \
doctest_detail_test_suite_ns::getCurrentTestSuite()) * \
- decorators); \
- DOCTEST_GLOBAL_NO_WARNINGS_END()
+ decorators))
#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \
- namespace { \
+ namespace { /* NOLINT */ \
struct der : public base \
{ \
void f(); \
}; \
- static void func() { \
+ static DOCTEST_INLINE_NOINLINE void func() { \
der v; \
v.f(); \
} \
DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \
} \
- inline DOCTEST_NOINLINE void der::f()
+ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \
static void f(); \
@@ -1875,18 +2183,18 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \
static doctest::detail::funcType proxy() { return f; } \
- DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \
+ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \
static void f()
// for registering tests
#define DOCTEST_TEST_CASE(decorators) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
// for registering tests in classes - requires C++17 for inline variables!
-#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L)
+#if DOCTEST_CPLUSPLUS >= 201703L
#define DOCTEST_TEST_CASE_CLASS(decorators) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \
decorators)
#else // DOCTEST_TEST_CASE_CLASS
#define DOCTEST_TEST_CASE_CLASS(...) \
@@ -1895,26 +2203,25 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// for registering tests with a fixture
#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \
- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
// for converting types to strings without the <typeinfo> header and demangling
-#define DOCTEST_TYPE_TO_STRING_IMPL(...) \
- template <> \
- inline const char* type_to_string<__VA_ARGS__>() { \
- return "<" #__VA_ARGS__ ">"; \
- }
-#define DOCTEST_TYPE_TO_STRING(...) \
- namespace doctest { namespace detail { \
- DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \
+ namespace doctest { \
+ template <> \
+ inline String toString<__VA_ARGS__>() { \
+ return str; \
} \
} \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ static_assert(true, "")
+
+#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__)
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \
template <typename T> \
static void func(); \
- namespace { \
+ namespace { /* NOLINT */ \
template <typename Tuple> \
struct iter; \
template <typename Type, typename... Rest> \
@@ -1923,7 +2230,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
iter(const char* file, unsigned line, int index) { \
doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \
doctest_detail_test_suite_ns::getCurrentTestSuite(), \
- doctest::detail::type_to_string<Type>(), \
+ doctest::toString<Type>(), \
int(line) * 1000 + index) \
* dec); \
iter<std::tuple<Rest...>>(file, line, index + 1); \
@@ -1940,20 +2247,20 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \
DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_))
#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \
- doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\
- DOCTEST_GLOBAL_NO_WARNINGS_END()
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \
+ doctest::detail::instantiationHelper( \
+ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0)))
#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
+ static_assert(true, "")
#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \
+ static_assert(true, "")
#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \
DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \
@@ -1962,17 +2269,17 @@ int registerReporter(const char* name, int priority, bool isReporter) {
static void anon()
#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \
- DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__)
+ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__)
// for subcases
#define DOCTEST_SUBCASE(name) \
- if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
doctest::detail::Subcase(name, __FILE__, __LINE__))
// for grouping tests in test suites by using code blocks
#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \
namespace ns_name { namespace doctest_detail_test_suite_ns { \
- static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \
+ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
@@ -1992,53 +2299,53 @@ int registerReporter(const char* name, int priority, bool isReporter) {
namespace ns_name
#define DOCTEST_TEST_SUITE(decorators) \
- DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_))
+ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_))
// for starting a testsuite block
#define DOCTEST_TEST_SUITE_BEGIN(decorators) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
- doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \
+ static_assert(true, "")
// for ending a testsuite block
#define DOCTEST_TEST_SUITE_END \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \
- doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \
+ using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
// for registering exception translators
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \
inline doctest::String translatorName(signature); \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \
- doctest::registerExceptionTranslator(translatorName); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerExceptionTranslator(translatorName)) \
doctest::String translatorName(signature)
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
- DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \
+ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
signature)
// for registering reporters
#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
- doctest::registerReporter<reporter>(name, priority, true); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, true)) \
+ static_assert(true, "")
// for registering listeners
#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \
- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \
- doctest::registerReporter<reporter>(name, priority, false); \
- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, false)) \
+ static_assert(true, "")
-// for logging
+// clang-format off
+// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557
#define DOCTEST_INFO(...) \
- DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \
+ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \
+ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \
__VA_ARGS__)
+// clang-format on
#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
[&](std::ostream* s_name) { \
doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
mb_name.m_stream = s_name; \
@@ -2048,16 +2355,18 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
mb * __VA_ARGS__; \
- DOCTEST_ASSERT_LOG_AND_REACT(mb); \
- } while(false)
+ if(mb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ mb.react(); \
+ } DOCTEST_FUNC_SCOPE_END
// clang-format off
-#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
-#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
// clang-format on
#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
@@ -2070,18 +2379,37 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \
doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
- << __VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \
+ << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \
DOCTEST_CLANG_SUPPRESS_WARNING_POP
#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY( \
+ DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
+ __VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
@@ -2095,6 +2423,14 @@ int registerReporter(const char* name, int priority, bool isReporter) {
doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
<< __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
+ doctest::detail::binary_assert<doctest::detail::binaryAssertComparison::comparison>( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
+ #__VA_ARGS__, __VA_ARGS__)
+
#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__)
@@ -2105,51 +2441,83 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
// clang-format off
-#define DOCTEST_WARN_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false)
-#define DOCTEST_CHECK_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false)
-#define DOCTEST_REQUIRE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false)
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false)
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false)
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false)
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
// clang-format on
+#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
+#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
+#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
+#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
+#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
+#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
+#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
+#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
+#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
+#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
+#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
+#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
if(!doctest::getContextOptions()->no_throw) { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #expr, #__VA_ARGS__, message); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
- } catch(const typename doctest::detail::remove_const< \
- typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
- _DOCTEST_RB.translateException(); \
- _DOCTEST_RB.m_threw_as = true; \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+ } catch(const typename doctest::detail::types::remove_const< \
+ typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\
+ DOCTEST_RB.translateException(); \
+ DOCTEST_RB.m_threw_as = true; \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
} \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END
#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
- do { \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
if(!doctest::getContextOptions()->no_throw) { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, expr_str, "", __VA_ARGS__); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
} \
- } while(false)
+ } DOCTEST_FUNC_SCOPE_END
#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
__LINE__, #__VA_ARGS__); \
try { \
DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
- } catch(...) { _DOCTEST_RB.translateException(); } \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
// clang-format off
#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
@@ -2172,166 +2540,23 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } while(false)
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } while(false)
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } while(false)
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false)
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false)
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false)
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } while(false)
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } while(false)
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } while(false)
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
// clang-format on
-#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY( \
- _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
- __VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
-
-#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
- do { \
- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #__VA_ARGS__); \
- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \
- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while(false)
-
-#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
- doctest::detail::binary_assert<doctest::detail::binaryAssertComparison::comparison>( \
- doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
-
-#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
- doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
- #__VA_ARGS__, __VA_ARGS__)
-
-#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
-
-#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
-#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
-#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
-#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
-#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
-#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
-#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
-#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
-#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
-#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
-#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
-#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
-#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
-#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
-#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
-#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
-#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
-#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
-
-#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
-#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
-#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
-#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
-#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
-#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
-
-#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
-
-#undef DOCTEST_WARN_THROWS
-#undef DOCTEST_CHECK_THROWS
-#undef DOCTEST_REQUIRE_THROWS
-#undef DOCTEST_WARN_THROWS_AS
-#undef DOCTEST_CHECK_THROWS_AS
-#undef DOCTEST_REQUIRE_THROWS_AS
-#undef DOCTEST_WARN_THROWS_WITH
-#undef DOCTEST_CHECK_THROWS_WITH
-#undef DOCTEST_REQUIRE_THROWS_WITH
-#undef DOCTEST_WARN_THROWS_WITH_AS
-#undef DOCTEST_CHECK_THROWS_WITH_AS
-#undef DOCTEST_REQUIRE_THROWS_WITH_AS
-#undef DOCTEST_WARN_NOTHROW
-#undef DOCTEST_CHECK_NOTHROW
-#undef DOCTEST_REQUIRE_NOTHROW
-
-#undef DOCTEST_WARN_THROWS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_MESSAGE
-#undef DOCTEST_WARN_THROWS_AS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_AS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE
-#undef DOCTEST_WARN_THROWS_WITH_MESSAGE
-#undef DOCTEST_CHECK_THROWS_WITH_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
-#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE
-#undef DOCTEST_WARN_NOTHROW_MESSAGE
-#undef DOCTEST_CHECK_NOTHROW_MESSAGE
-#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE
-
-#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
-#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-
-#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
-#undef DOCTEST_REQUIRE
-#undef DOCTEST_REQUIRE_FALSE
-#undef DOCTEST_REQUIRE_MESSAGE
-#undef DOCTEST_REQUIRE_FALSE_MESSAGE
-#undef DOCTEST_REQUIRE_EQ
-#undef DOCTEST_REQUIRE_NE
-#undef DOCTEST_REQUIRE_GT
-#undef DOCTEST_REQUIRE_LT
-#undef DOCTEST_REQUIRE_GE
-#undef DOCTEST_REQUIRE_LE
-#undef DOCTEST_REQUIRE_UNARY
-#undef DOCTEST_REQUIRE_UNARY_FALSE
-
-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
// =================================================================================================
@@ -2341,7 +2566,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#else // DOCTEST_CONFIG_DISABLE
#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
- namespace { \
+ namespace /* NOLINT */ { \
template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
struct der : public base \
{ void f(); }; \
@@ -2355,51 +2580,48 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// for registering tests
#define DOCTEST_TEST_CASE(name) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for registering tests in classes
#define DOCTEST_TEST_CASE_CLASS(name) \
- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for registering tests with a fixture
#define DOCTEST_TEST_CASE_FIXTURE(x, name) \
- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \
- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
// for converting types to strings without the <typeinfo> header and demangling
-#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
-#define DOCTEST_TYPE_TO_STRING_IMPL(...)
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "")
+#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "")
// for typed tests
#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \
template <typename type> \
- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \
template <typename type> \
- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
-#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
-
-#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "")
+#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "")
// for subcases
#define DOCTEST_SUBCASE(name)
// for a testsuite block
-#define DOCTEST_TEST_SUITE(name) namespace
+#define DOCTEST_TEST_SUITE(name) namespace // NOLINT
// for starting a testsuite block
-#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "")
// for ending a testsuite block
-#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
- static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
+ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature)
#define DOCTEST_REGISTER_REPORTER(name, priority, reporter)
#define DOCTEST_REGISTER_LISTENER(name, priority, reporter)
@@ -2413,80 +2635,253 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0))
#define DOCTEST_FAIL(...) (static_cast<void>(0))
-#define DOCTEST_WARN(...) (static_cast<void>(0))
-#define DOCTEST_CHECK(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_FALSE(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_EQ(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_EQ(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_EQ(...) (static_cast<void>(0))
-#define DOCTEST_WARN_NE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_NE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_NE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_GT(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_GT(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_GT(...) (static_cast<void>(0))
-#define DOCTEST_WARN_LT(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_LT(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_LT(...) (static_cast<void>(0))
-#define DOCTEST_WARN_GE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_GE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_GE(...) (static_cast<void>(0))
-#define DOCTEST_WARN_LE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_LE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_LE(...) (static_cast<void>(0))
-
-#define DOCTEST_WARN_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_UNARY(...) (static_cast<void>(0))
-#define DOCTEST_WARN_UNARY_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_CHECK_UNARY_FALSE(...) (static_cast<void>(0))
-#define DOCTEST_REQUIRE_UNARY_FALSE(...) (static_cast<void>(0))
+#if defined(DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED) \
+ && defined(DOCTEST_CONFIG_ASSERTS_RETURN_VALUES)
+
+#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }()
+
+#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }()
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }()
+
+namespace doctest {
+namespace detail {
+#define DOCTEST_RELATIONAL_OP(name, op) \
+ template <typename L, typename R> \
+ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; }
+
+ DOCTEST_RELATIONAL_OP(eq, ==)
+ DOCTEST_RELATIONAL_OP(ne, !=)
+ DOCTEST_RELATIONAL_OP(lt, <)
+ DOCTEST_RELATIONAL_OP(gt, >)
+ DOCTEST_RELATIONAL_OP(le, <=)
+ DOCTEST_RELATIONAL_OP(ge, >=)
+} // namespace detail
+} // namespace doctest
+
+#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }()
+#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }()
+#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }()
+#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }()
+#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }()
+#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }()
+#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }()
+#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }()
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_WARN_THROWS_WITH(expr, with, ...) [] { static_assert(false, "Exception translation is not available when doctest is disabled."); return false; }()
+#define DOCTEST_CHECK_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,)
+
+#define DOCTEST_WARN_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_CHECK_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_REQUIRE_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_WARN_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_WARN_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_CHECK_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_REQUIRE_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }()
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }()
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }()
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
+
+#define DOCTEST_WARN(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_LE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_LE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_WARN_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_FUNC_EMPTY
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED
#endif // DOCTEST_CONFIG_DISABLE
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#define DOCTEST_EXCEPTION_EMPTY_FUNC DOCTEST_FUNC_EMPTY
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#define DOCTEST_EXCEPTION_EMPTY_FUNC [] { static_assert(false, "Exceptions are disabled! " \
+ "Use DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS if you want to compile with exceptions disabled."); return false; }()
+
+#undef DOCTEST_REQUIRE
+#undef DOCTEST_REQUIRE_FALSE
+#undef DOCTEST_REQUIRE_MESSAGE
+#undef DOCTEST_REQUIRE_FALSE_MESSAGE
+#undef DOCTEST_REQUIRE_EQ
+#undef DOCTEST_REQUIRE_NE
+#undef DOCTEST_REQUIRE_GT
+#undef DOCTEST_REQUIRE_LT
+#undef DOCTEST_REQUIRE_GE
+#undef DOCTEST_REQUIRE_LE
+#undef DOCTEST_REQUIRE_UNARY
+#undef DOCTEST_REQUIRE_UNARY_FALSE
+
+#define DOCTEST_REQUIRE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_FALSE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_EQ DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_GT DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_LT DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_GE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_LE DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_UNARY DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_UNARY_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#define DOCTEST_WARN_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
// clang-format off
// KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS
#define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ
@@ -2533,11 +2928,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// clang-format on
// == SHORT VERSIONS OF THE MACROS
-#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
+#ifndef DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
#define TEST_CASE(name) DOCTEST_TEST_CASE(name)
#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name)
#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name)
+#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__)
#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__)
#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
@@ -2670,37 +3066,17 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
-#if !defined(DOCTEST_CONFIG_DISABLE)
+#ifndef DOCTEST_CONFIG_DISABLE
// this is here to clear the 'current test suite' for the current translation unit - at the top
DOCTEST_TEST_SUITE_END();
-// add stringification for primitive/fundamental types
-namespace doctest { namespace detail {
- DOCTEST_TYPE_TO_STRING_IMPL(bool)
- DOCTEST_TYPE_TO_STRING_IMPL(float)
- DOCTEST_TYPE_TO_STRING_IMPL(double)
- DOCTEST_TYPE_TO_STRING_IMPL(long double)
- DOCTEST_TYPE_TO_STRING_IMPL(char)
- DOCTEST_TYPE_TO_STRING_IMPL(signed char)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned char)
-#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED)
- DOCTEST_TYPE_TO_STRING_IMPL(wchar_t)
-#endif // not MSVC or wchar_t support enabled
- DOCTEST_TYPE_TO_STRING_IMPL(short int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int)
- DOCTEST_TYPE_TO_STRING_IMPL(int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned int)
- DOCTEST_TYPE_TO_STRING_IMPL(long int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int)
- DOCTEST_TYPE_TO_STRING_IMPL(long long int)
- DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int)
-}} // namespace doctest::detail
-
#endif // DOCTEST_CONFIG_DISABLE
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
+DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
+
#endif // DOCTEST_LIBRARY_INCLUDED
diff --git a/contrib/doctest/scripts/cmake/Config.cmake.in b/contrib/doctest/scripts/cmake/Config.cmake.in
new file mode 100644
index 000000000..44ddb07d8
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/Config.cmake.in
@@ -0,0 +1,6 @@
+if(NOT TARGET doctest::doctest)
+ # Provide path for scripts
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
+
+ include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
+endif()
diff --git a/contrib/doctest/scripts/cmake/assemble_single_header.cmake b/contrib/doctest/scripts/cmake/assemble_single_header.cmake
new file mode 100644
index 000000000..980975491
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/assemble_single_header.cmake
@@ -0,0 +1,13 @@
+set(doctest_include_folder "${CMAKE_CURRENT_LIST_DIR}/../../doctest/")
+
+file(READ ${doctest_include_folder}/parts/doctest_fwd.h fwd)
+file(READ ${doctest_include_folder}/parts/doctest.cpp impl)
+
+file(WRITE ${doctest_include_folder}/doctest.h "// ====================================================================== lgtm [cpp/missing-header-guard]\n")
+file(APPEND ${doctest_include_folder}/doctest.h "// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==\n")
+file(APPEND ${doctest_include_folder}/doctest.h "// ======================================================================\n")
+file(APPEND ${doctest_include_folder}/doctest.h "${fwd}\n")
+file(APPEND ${doctest_include_folder}/doctest.h "#ifndef DOCTEST_SINGLE_HEADER\n")
+file(APPEND ${doctest_include_folder}/doctest.h "#define DOCTEST_SINGLE_HEADER\n")
+file(APPEND ${doctest_include_folder}/doctest.h "#endif // DOCTEST_SINGLE_HEADER\n")
+file(APPEND ${doctest_include_folder}/doctest.h "\n${impl}")
diff --git a/contrib/doctest/scripts/cmake/common.cmake b/contrib/doctest/scripts/cmake/common.cmake
new file mode 100644
index 000000000..b90f5dd75
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/common.cmake
@@ -0,0 +1,212 @@
+include(CMakeParseArguments)
+
+# cache this for use inside of the function
+set(CURRENT_LIST_DIR_CACHED ${CMAKE_CURRENT_LIST_DIR})
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+enable_testing()
+
+find_package(Threads)
+
+set(DOCTEST_TEST_MODE "COMPARE" CACHE STRING "Test mode - normal/run through valgrind/collect output/compare with output")
+set_property(CACHE DOCTEST_TEST_MODE PROPERTY STRINGS "NORMAL;VALGRIND;COLLECT;COMPARE")
+
+function(doctest_add_test_impl)
+ cmake_parse_arguments(ARG "NO_VALGRIND;NO_OUTPUT;XML_OUTPUT;JUNIT_OUTPUT" "NAME" "COMMAND" ${ARGN})
+ if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "" OR "${ARG_NAME}" STREQUAL "" OR "${ARG_COMMAND}" STREQUAL "")
+ message(FATAL_ERROR "doctest_add_test() called with wrong options!")
+ endif()
+
+ set(the_test_mode NORMAL)
+
+ # construct the command that will be called by the exec_test.cmake script
+ set(the_command "")
+ if(${DOCTEST_TEST_MODE} STREQUAL "VALGRIND" AND NOT ARG_NO_VALGRIND)
+ set(the_test_mode VALGRIND)
+ set(the_command "valgrind -v --leak-check=full --track-origins=yes --error-exitcode=1")
+ endif()
+ foreach(cur ${ARG_COMMAND})
+ set(the_command "${the_command} ${cur}")
+ endforeach()
+ if(ARG_XML_OUTPUT)
+ set(the_command "${the_command} --reporters=xml")
+ set(ARG_NAME ${ARG_NAME}_xml)
+ endif()
+ if(ARG_JUNIT_OUTPUT)
+ set(the_command "${the_command} --reporters=junit")
+ set(ARG_NAME ${ARG_NAME}_junit)
+ endif()
+
+ # append the argument for removing paths from filenames in the output so tests give the same output everywhere
+ set(the_command "${the_command} --dt-no-path-filenames=1")
+ # append the argument for substituting source line numbers with 0 in the output so tests give the same output when lines change a bit
+ set(the_command "${the_command} --dt-no-line-numbers=1")
+ # append the argument for ignoring the exit code of the test programs because some are intended to have failing tests
+ set(the_command "${the_command} --dt-no-exitcode=1")
+ # append the argument for using the same line format in the output - so gcc/non-gcc builds have the same output
+ set(the_command "${the_command} --dt-gnu-file-line=0")
+ # append the argument for skipping any time-related output so that the reference output from reporters is stable on CI
+ set(the_command "${the_command} --dt-no-time-in-output=1")
+
+ string(STRIP ${the_command} the_command)
+
+ if(${DOCTEST_TEST_MODE} STREQUAL "COLLECT" OR ${DOCTEST_TEST_MODE} STREQUAL "COMPARE")
+ if(NOT ARG_NO_OUTPUT)
+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_output/)
+ set(the_test_mode ${DOCTEST_TEST_MODE})
+ list(APPEND ADDITIONAL_FLAGS -DTEST_OUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/test_output/${ARG_NAME}.txt)
+ list(APPEND ADDITIONAL_FLAGS -DTEST_TEMP_FILE=${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/temp_test_output_${ARG_NAME}.txt)
+ endif()
+ endif()
+
+ list(APPEND ADDITIONAL_FLAGS -DTEST_MODE=${the_test_mode})
+
+ add_test(NAME ${ARG_NAME} COMMAND ${CMAKE_COMMAND} -DCOMMAND=${the_command} ${ADDITIONAL_FLAGS} -P ${CURRENT_LIST_DIR_CACHED}/exec_test.cmake)
+endfunction()
+
+# a custom version of add_test() to suite my needs
+function(doctest_add_test)
+ doctest_add_test_impl(${ARGN})
+ doctest_add_test_impl(${ARGN} XML_OUTPUT)
+ doctest_add_test_impl(${ARGN} JUNIT_OUTPUT)
+endfunction()
+
+macro(add_compiler_flags)
+ foreach(flag ${ARGV})
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}")
+ endforeach()
+endmacro()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ add_compiler_flags(-Werror)
+ add_compiler_flags(-fstrict-aliasing)
+
+ # The following options are not valid when clang-cl is used.
+ if(NOT MSVC)
+ add_compiler_flags(-pedantic)
+ add_compiler_flags(-pedantic-errors)
+ add_compiler_flags(-fvisibility=hidden)
+ endif()
+endif()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ #add_compiler_flags(-Wno-unknown-pragmas)
+ add_compiler_flags(-Wall)
+ add_compiler_flags(-Wextra)
+ add_compiler_flags(-fdiagnostics-show-option)
+ add_compiler_flags(-Wconversion)
+ add_compiler_flags(-Wold-style-cast)
+ add_compiler_flags(-Wfloat-equal)
+ add_compiler_flags(-Wlogical-op)
+ add_compiler_flags(-Wundef)
+ add_compiler_flags(-Wredundant-decls)
+ add_compiler_flags(-Wshadow)
+ add_compiler_flags(-Wstrict-overflow=5)
+ add_compiler_flags(-Wwrite-strings)
+ add_compiler_flags(-Wpointer-arith)
+ add_compiler_flags(-Wcast-qual)
+ add_compiler_flags(-Wformat=2)
+ add_compiler_flags(-Wswitch-default)
+ add_compiler_flags(-Wmissing-include-dirs)
+ add_compiler_flags(-Wcast-align)
+ add_compiler_flags(-Wswitch-enum)
+ add_compiler_flags(-Wnon-virtual-dtor)
+ add_compiler_flags(-Wctor-dtor-privacy)
+ add_compiler_flags(-Wsign-conversion)
+ add_compiler_flags(-Wdisabled-optimization)
+ add_compiler_flags(-Weffc++)
+ add_compiler_flags(-Winvalid-pch)
+ add_compiler_flags(-Wmissing-declarations)
+ add_compiler_flags(-Woverloaded-virtual)
+ add_compiler_flags(-Wunused-but-set-variable)
+ add_compiler_flags(-Wunused-result)
+
+ # add_compiler_flags(-Wsuggest-override)
+ # add_compiler_flags(-Wmultiple-inheritance)
+ # add_compiler_flags(-Wcatch-value)
+ # add_compiler_flags(-Wsuggest-attribute=cold)
+ # add_compiler_flags(-Wsuggest-attribute=const)
+ # add_compiler_flags(-Wsuggest-attribute=format)
+ # add_compiler_flags(-Wsuggest-attribute=malloc)
+ # add_compiler_flags(-Wsuggest-attribute=noreturn)
+ # add_compiler_flags(-Wsuggest-attribute=pure)
+ # add_compiler_flags(-Wsuggest-final-methods)
+ # add_compiler_flags(-Wsuggest-final-types)
+
+ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
+ add_compiler_flags(-Wnoexcept)
+ endif()
+
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
+ add_compiler_flags(-Wno-missing-field-initializers)
+ endif()
+
+ # no way to silence it in the expression decomposition macros: _Pragma() in macros doesn't work for the c++ front-end of g++
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543
+ # Also the warning is completely worthless nowadays - https://stackoverflow.com/questions/14016993
+ #add_compiler_flags(-Waggregate-return)
+
+ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
+ add_compiler_flags(-Wdouble-promotion)
+ add_compiler_flags(-Wtrampolines)
+ add_compiler_flags(-Wzero-as-null-pointer-constant)
+ add_compiler_flags(-Wuseless-cast)
+ add_compiler_flags(-Wvector-operation-performance)
+ endif()
+
+ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
+ add_compiler_flags(-Wshift-overflow=2)
+ add_compiler_flags(-Wnull-dereference)
+ add_compiler_flags(-Wduplicated-cond)
+ endif()
+
+ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
+ add_compiler_flags(-Walloc-zero)
+ add_compiler_flags(-Walloca)
+ add_compiler_flags(-Wduplicated-branches)
+ endif()
+
+ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0)
+ add_compiler_flags(-Wcast-align=strict)
+ endif()
+endif()
+
+# necessary for some older compilers which don't default to C++11
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ add_compiler_flags(-Weverything)
+ add_compiler_flags(-Wno-c++98-compat)
+ add_compiler_flags(-Wno-c++98-compat-pedantic)
+ add_compiler_flags(-Wno-c++98-compat-bind-to-temporary-copy)
+ add_compiler_flags(-Wno-c++98-compat-local-type-template-args)
+ add_compiler_flags(-Qunused-arguments -fcolor-diagnostics) # needed for ccache integration
+endif()
+
+if(MSVC)
+ add_compiler_flags(/std:c++latest) # for post c++14 updates in MSVC
+ add_compiler_flags(/permissive-) # force standard conformance - this is the better flag than /Za
+ add_compiler_flags(/WX)
+ add_compiler_flags(/Wall) # turns on warnings from levels 1 through 4 which are off by default - https://msdn.microsoft.com/en-us/library/23k5d385.aspx
+
+ add_compiler_flags(
+ /wd4514 # unreferenced inline function has been removed
+ /wd4571 # SEH related
+ /wd5264 # const variable is not used
+ /wd4710 # function not inlined
+ /wd4711 # function 'x' selected for automatic inline expansion
+
+ /wd4616 # invalid compiler warnings - https://msdn.microsoft.com/en-us/library/t7ab6xtd.aspx
+ /wd4619 # invalid compiler warnings - https://msdn.microsoft.com/en-us/library/tacee08d.aspx
+
+ #/wd4820 # padding in structs
+ #/wd4625 # copy constructor was implicitly defined as deleted
+ #/wd4626 # assignment operator was implicitly defined as deleted
+ #/wd5027 # move assignment operator was implicitly defined as deleted
+ #/wd5026 # move constructor was implicitly defined as deleted
+ #/wd4623 # default constructor was implicitly defined as deleted
+ )
+endif()
diff --git a/contrib/doctest/scripts/cmake/doctest.cmake b/contrib/doctest/scripts/cmake/doctest.cmake
new file mode 100644
index 000000000..3c4929f20
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/doctest.cmake
@@ -0,0 +1,189 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+doctest
+-----
+
+This module defines a function to help use the doctest test framework.
+
+The :command:`doctest_discover_tests` discovers tests by asking the compiled test
+executable to enumerate its tests. This does not require CMake to be re-run
+when tests change. However, it may not work in a cross-compiling environment,
+and setting test properties is less convenient.
+
+This command is intended to replace use of :command:`add_test` to register
+tests, and will create a separate CTest test for each doctest test case. Note
+that this is in some cases less efficient, as common set-up and tear-down logic
+cannot be shared by multiple test cases executing in the same instance.
+However, it provides more fine-grained pass/fail information to CTest, which is
+usually considered as more beneficial. By default, the CTest test name is the
+same as the doctest name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
+
+.. command:: doctest_discover_tests
+
+ Automatically add tests with CTest by querying the compiled test executable
+ for available tests::
+
+ doctest_discover_tests(target
+ [TEST_SPEC arg1...]
+ [EXTRA_ARGS arg1...]
+ [WORKING_DIRECTORY dir]
+ [TEST_PREFIX prefix]
+ [TEST_SUFFIX suffix]
+ [PROPERTIES name1 value1...]
+ [ADD_LABELS value]
+ [TEST_LIST var]
+ [JUNIT_OUTPUT_DIR dir]
+ )
+
+ ``doctest_discover_tests`` sets up a post-build command on the test executable
+ that generates the list of tests by parsing the output from running the test
+ with the ``--list-test-cases`` argument. This ensures that the full
+ list of tests is obtained. Since test discovery occurs at build time, it is
+ not necessary to re-run CMake when the list of tests changes.
+ However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
+ in order to function in a cross-compiling environment.
+
+ Additionally, setting properties on tests is somewhat less convenient, since
+ the tests are not available at CMake time. Additional test properties may be
+ assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
+ more fine-grained test control is needed, custom content may be provided
+ through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
+ directory property. The set of discovered tests is made accessible to such a
+ script via the ``<target>_TESTS`` variable.
+
+ The options are:
+
+ ``target``
+ Specifies the doctest executable, which must be a known CMake executable
+ target. CMake will substitute the location of the built executable when
+ running the test.
+
+ ``TEST_SPEC arg1...``
+ Specifies test cases, wildcarded test cases, tags and tag expressions to
+ pass to the doctest executable with the ``--list-test-cases`` argument.
+
+ ``EXTRA_ARGS arg1...``
+ Any extra arguments to pass on the command line to each test case.
+
+ ``WORKING_DIRECTORY dir``
+ Specifies the directory in which to run the discovered test cases. If this
+ option is not provided, the current binary directory is used.
+
+ ``TEST_PREFIX prefix``
+ Specifies a ``prefix`` to be prepended to the name of each discovered test
+ case. This can be useful when the same test executable is being used in
+ multiple calls to ``doctest_discover_tests()`` but with different
+ ``TEST_SPEC`` or ``EXTRA_ARGS``.
+
+ ``TEST_SUFFIX suffix``
+ Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
+ every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
+ be specified.
+
+ ``PROPERTIES name1 value1...``
+ Specifies additional properties to be set on all tests discovered by this
+ invocation of ``doctest_discover_tests``.
+
+ ``ADD_LABELS value``
+ Specifies if the test labels should be set automatically.
+
+ ``TEST_LIST var``
+ Make the list of tests available in the variable ``var``, rather than the
+ default ``<target>_TESTS``. This can be useful when the same test
+ executable is being used in multiple calls to ``doctest_discover_tests()``.
+ Note that this variable is only available in CTest.
+
+ ``JUNIT_OUTPUT_DIR dir``
+ If specified, the parameter is passed along with ``--reporters=junit``
+ and ``--out=`` to the test executable. The actual file name is the same
+ as the test target, including prefix and suffix. This should be used
+ instead of EXTRA_ARGS to avoid race conditions writing the XML result
+ output when using parallel test execution.
+
+#]=======================================================================]
+
+#------------------------------------------------------------------------------
+function(doctest_discover_tests TARGET)
+ cmake_parse_arguments(
+ ""
+ ""
+ "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;JUNIT_OUTPUT_DIR"
+ "TEST_SPEC;EXTRA_ARGS;PROPERTIES;ADD_LABELS"
+ ${ARGN}
+ )
+
+ if(NOT _WORKING_DIRECTORY)
+ set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ if(NOT _TEST_LIST)
+ set(_TEST_LIST ${TARGET}_TESTS)
+ endif()
+
+ ## Generate a unique name based on the extra arguments
+ string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
+ string(SUBSTRING ${args_hash} 0 7 args_hash)
+
+ # Define rule to generate test list for aforementioned test executable
+ set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
+ set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
+ get_property(crosscompiling_emulator
+ TARGET ${TARGET}
+ PROPERTY CROSSCOMPILING_EMULATOR
+ )
+ add_custom_command(
+ TARGET ${TARGET} POST_BUILD
+ BYPRODUCTS "${ctest_tests_file}"
+ COMMAND "${CMAKE_COMMAND}"
+ -D "TEST_TARGET=${TARGET}"
+ -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
+ -D "TEST_EXECUTOR=${crosscompiling_emulator}"
+ -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
+ -D "TEST_SPEC=${_TEST_SPEC}"
+ -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
+ -D "TEST_PROPERTIES=${_PROPERTIES}"
+ -D "TEST_ADD_LABELS=${_ADD_LABELS}"
+ -D "TEST_PREFIX=${_TEST_PREFIX}"
+ -D "TEST_SUFFIX=${_TEST_SUFFIX}"
+ -D "TEST_LIST=${_TEST_LIST}"
+ -D "TEST_JUNIT_OUTPUT_DIR=${_JUNIT_OUTPUT_DIR}"
+ -D "CTEST_FILE=${ctest_tests_file}"
+ -P "${_DOCTEST_DISCOVER_TESTS_SCRIPT}"
+ VERBATIM
+ )
+
+ file(WRITE "${ctest_include_file}"
+ "if(EXISTS \"${ctest_tests_file}\")\n"
+ " include(\"${ctest_tests_file}\")\n"
+ "else()\n"
+ " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
+ "endif()\n"
+ )
+
+ if(NOT CMAKE_VERSION VERSION_LESS 3.10)
+ # Add discovered tests to directory TEST_INCLUDE_FILES
+ set_property(DIRECTORY
+ APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
+ )
+ else()
+ # Add discovered tests as directory TEST_INCLUDE_FILE if possible
+ get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
+ if(NOT ${test_include_file_set})
+ set_property(DIRECTORY
+ PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
+ )
+ else()
+ message(FATAL_ERROR
+ "Cannot set more than one TEST_INCLUDE_FILE"
+ )
+ endif()
+ endif()
+
+endfunction()
+
+###############################################################################
+
+set(_DOCTEST_DISCOVER_TESTS_SCRIPT
+ ${CMAKE_CURRENT_LIST_DIR}/doctestAddTests.cmake
+)
diff --git a/contrib/doctest/scripts/cmake/doctestAddTests.cmake b/contrib/doctest/scripts/cmake/doctestAddTests.cmake
new file mode 100644
index 000000000..3b254850f
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/doctestAddTests.cmake
@@ -0,0 +1,120 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+set(prefix "${TEST_PREFIX}")
+set(suffix "${TEST_SUFFIX}")
+set(spec ${TEST_SPEC})
+set(extra_args ${TEST_EXTRA_ARGS})
+set(properties ${TEST_PROPERTIES})
+set(add_labels ${TEST_ADD_LABELS})
+set(junit_output_dir "${TEST_JUNIT_OUTPUT_DIR}")
+set(script)
+set(suite)
+set(tests)
+
+function(add_command NAME)
+ set(_args "")
+ foreach(_arg ${ARGN})
+ if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
+ set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
+ else()
+ set(_args "${_args} ${_arg}")
+ endif()
+ endforeach()
+ set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
+endfunction()
+
+# Run test executable to get list of available tests
+if(NOT EXISTS "${TEST_EXECUTABLE}")
+ message(FATAL_ERROR
+ "Specified test executable '${TEST_EXECUTABLE}' does not exist"
+ )
+endif()
+
+if("${spec}" MATCHES .)
+ set(spec "--test-case=${spec}")
+endif()
+
+execute_process(
+ COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-cases
+ OUTPUT_VARIABLE output
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY "${TEST_WORKING_DIR}"
+)
+if(NOT ${result} EQUAL 0)
+ message(FATAL_ERROR
+ "Error running test executable '${TEST_EXECUTABLE}':\n"
+ " Result: ${result}\n"
+ " Output: ${output}\n"
+ )
+endif()
+
+string(REPLACE "\n" ";" output "${output}")
+
+# Parse output
+foreach(line ${output})
+ if("${line}" STREQUAL "===============================================================================" OR "${line}" MATCHES [==[^\[doctest\] ]==])
+ continue()
+ endif()
+ set(test ${line})
+ set(labels "")
+ if(${add_labels})
+ # get test suite that test belongs to
+ execute_process(
+ COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --test-case=${test} --list-test-suites
+ OUTPUT_VARIABLE labeloutput
+ RESULT_VARIABLE labelresult
+ WORKING_DIRECTORY "${TEST_WORKING_DIR}"
+ )
+ if(NOT ${labelresult} EQUAL 0)
+ message(FATAL_ERROR
+ "Error running test executable '${TEST_EXECUTABLE}':\n"
+ " Result: ${labelresult}\n"
+ " Output: ${labeloutput}\n"
+ )
+ endif()
+
+ string(REPLACE "\n" ";" labeloutput "${labeloutput}")
+ foreach(labelline ${labeloutput})
+ if("${labelline}" STREQUAL "===============================================================================" OR "${labelline}" MATCHES [==[^\[doctest\] ]==])
+ continue()
+ endif()
+ list(APPEND labels ${labelline})
+ endforeach()
+ endif()
+
+ if(NOT "${junit_output_dir}" STREQUAL "")
+ # turn testname into a valid filename by replacing all special characters with "-"
+ string(REGEX REPLACE "[/\\:\"|<>]" "-" test_filename "${test}")
+ set(TEST_JUNIT_OUTPUT_PARAM "--reporters=junit" "--out=${junit_output_dir}/${prefix}${test_filename}${suffix}.xml")
+ else()
+ unset(TEST_JUNIT_OUTPUT_PARAM)
+ endif()
+ # use escape commas to handle properly test cases with commas inside the name
+ string(REPLACE "," "\\," test_name ${test})
+ # ...and add to script
+ add_command(add_test
+ "${prefix}${test}${suffix}"
+ ${TEST_EXECUTOR}
+ "${TEST_EXECUTABLE}"
+ "--test-case=${test_name}"
+ "${TEST_JUNIT_OUTPUT_PARAM}"
+ ${extra_args}
+ )
+ add_command(set_tests_properties
+ "${prefix}${test}${suffix}"
+ PROPERTIES
+ WORKING_DIRECTORY "${TEST_WORKING_DIR}"
+ ${properties}
+ LABELS ${labels}
+ )
+ unset(labels)
+ list(APPEND tests "${prefix}${test}${suffix}")
+endforeach()
+
+# Create a list of all discovered tests, which users may use to e.g. set
+# properties on the tests
+add_command(set ${TEST_LIST} ${tests})
+
+# Write CTest script
+file(WRITE "${CTEST_FILE}" "${script}")
diff --git a/contrib/doctest/scripts/cmake/exec_test.cmake b/contrib/doctest/scripts/cmake/exec_test.cmake
new file mode 100644
index 000000000..ab51a0157
--- /dev/null
+++ b/contrib/doctest/scripts/cmake/exec_test.cmake
@@ -0,0 +1,70 @@
+# Arguments:
+# - COMMAND: the command to run with all it's arguments
+# - TEST_MODE: NORMAL/VALGRIND/COLLECT/COMPARE
+# - TEST_OUTPUT_FILE: the file to/from which to write/read the output of the test
+# - TEST_TEMP_FILE: the temp file for the current test output used in COMPARE mode
+# To run something through this script use cmake like this:
+# cmake -DCOMMAND=path/to/my.exe -arg1 -arg2 -DTEST_MODE=VALGRIND -P path/to/exec_test.cmake
+
+#message("COMMAND: ${COMMAND}")
+#message("TEST_MODE: ${TEST_MODE}")
+#message("TEST_OUTPUT_FILE: ${TEST_OUTPUT_FILE}")
+#message("TEST_TEMP_FILE: ${TEST_TEMP_FILE}")
+
+string(REPLACE " " ";" COMMAND_LIST ${COMMAND})
+set(cmd COMMAND ${COMMAND_LIST} RESULT_VARIABLE CMD_RESULT)
+if("${TEST_MODE}" STREQUAL "COLLECT")
+ list(APPEND cmd OUTPUT_FILE ${TEST_OUTPUT_FILE} ERROR_FILE ${TEST_OUTPUT_FILE})
+elseif("${TEST_MODE}" STREQUAL "COMPARE")
+ list(APPEND cmd OUTPUT_FILE ${TEST_TEMP_FILE} ERROR_FILE ${TEST_TEMP_FILE})
+endif()
+
+execute_process(${cmd})
+
+# fix line endings
+if("${TEST_MODE}" STREQUAL "COLLECT" AND NOT CMAKE_HOST_UNIX)
+ execute_process(COMMAND dos2unix ${TEST_OUTPUT_FILE})
+endif()
+
+if("${TEST_MODE}" STREQUAL "COMPARE")
+ # fix line endings
+ if(NOT CMAKE_HOST_UNIX)
+ execute_process(COMMAND dos2unix ${TEST_TEMP_FILE})
+ endif()
+
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.14.0")
+ set(IGNORE_EOL --ignore-eol)
+ endif()
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files ${IGNORE_EOL} ${TEST_OUTPUT_FILE} ${TEST_TEMP_FILE} RESULT_VARIABLE cmp_result)
+
+ if(cmp_result)
+ find_package(Git)
+ if(GIT_FOUND)
+ set(cmd ${GIT_EXECUTABLE} diff --no-index ${TEST_OUTPUT_FILE} ${TEST_TEMP_FILE})
+ execute_process(COMMAND ${GIT_EXECUTABLE} diff --no-index ${TEST_OUTPUT_FILE} ${TEST_TEMP_FILE} OUTPUT_VARIABLE DIFF)
+ message("${DIFF}")
+ endif()
+
+ # file(READ ${TEST_OUTPUT_FILE} orig)
+ # file(READ ${TEST_TEMP_FILE} temp)
+
+ # message("==========================================================================")
+ # message("== CONTENTS OF ${TEST_OUTPUT_FILE}")
+ # message("==========================================================================")
+ # message("${orig}")
+ # message("==========================================================================")
+ # message("== CONTENTS OF ${TEST_TEMP_FILE}")
+ # message("==========================================================================")
+ # message("${temp}")
+ # message("==========================================================================")
+ # message("== CONTENTS END")
+ # message("==========================================================================")
+
+ set(CMD_RESULT "Output is different from reference file!")
+ endif()
+endif()
+
+if(CMD_RESULT)
+ message(FATAL_ERROR "Running '${COMMAND}' ended with code '${CMD_RESULT}'")
+endif()
diff --git a/contrib/doctest/scripts/version.txt b/contrib/doctest/scripts/version.txt
new file mode 100644
index 000000000..e393c3c55
--- /dev/null
+++ b/contrib/doctest/scripts/version.txt
@@ -0,0 +1 @@
+2.4.11 \ No newline at end of file
diff --git a/contrib/kann/kann.c b/contrib/kann/kann.c
index 70d1f02d6..658f98a44 100644
--- a/contrib/kann/kann.c
+++ b/contrib/kann/kann.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2024 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "config.h"
#include <math.h>
@@ -19,9 +35,9 @@ static void kad_ext_collate(int n, kad_node_t **a, float **_x, float **_g, float
int i, j, k, l, n_var;
float *x, *g, *c;
n_var = kad_size_var(n, a);
- x = *_x = (float*)realloc(*_x, n_var * sizeof(float));
- g = *_g = (float*)realloc(*_g, n_var * sizeof(float));
- c = *_c = (float*)realloc(*_c, kad_size_const(n, a) * sizeof(float));
+ x = *_x = (float *) realloc(*_x, n_var * sizeof(float));
+ g = *_g = (float *) realloc(*_g, n_var * sizeof(float));
+ c = *_c = (float *) realloc(*_c, kad_size_const(n, a) * sizeof(float));
memset(g, 0, n_var * sizeof(float));
for (i = j = k = 0; i < n; ++i) {
kad_node_t *v = a[i];
@@ -32,7 +48,8 @@ static void kad_ext_collate(int n, kad_node_t **a, float **_x, float **_g, float
v->x = &x[j];
v->g = &g[j];
j += l;
- } else if (kad_is_const(v)) {
+ }
+ else if (kad_is_const(v)) {
l = kad_len(v);
memcpy(&c[k], v->x, l * sizeof(float));
free(v->x);
@@ -51,7 +68,8 @@ static void kad_ext_sync(int n, kad_node_t **a, float *x, float *g, float *c)
v->x = &x[j];
v->g = &g[j];
j += kad_len(v);
- } else if (kad_is_const(v)) {
+ }
+ else if (kad_is_const(v)) {
v->x = &c[k];
k += kad_len(v);
}
@@ -68,14 +86,14 @@ kann_t *kann_new(kad_node_t *cost, int n_rest, ...)
if (cost->n_d != 0) return 0;
va_start(ap, n_rest);
- roots = (kad_node_t**)malloc((n_roots + 1) * sizeof(kad_node_t*));
+ roots = (kad_node_t **) malloc((n_roots + 1) * sizeof(kad_node_t *));
for (i = 0; i < n_rest; ++i)
- roots[i] = va_arg(ap, kad_node_t*);
+ roots[i] = va_arg(ap, kad_node_t *);
roots[i++] = cost;
va_end(ap);
cost->ext_flag |= KANN_F_COST;
- a = (kann_t*)calloc(1, sizeof(kann_t));
+ a = (kann_t *) calloc(1, sizeof(kann_t));
a->v = kad_compile_array(&a->n, n_roots, roots);
for (i = 0; i < a->n; ++i) {
@@ -84,7 +102,7 @@ kann_t *kann_new(kad_node_t *cost, int n_rest, ...)
}
if (has_recur && !has_pivot) { /* an RNN that doesn't have a pivot; then add a pivot on top of cost and recompile */
cost->ext_flag &= ~KANN_F_COST;
- roots[n_roots-1] = cost = kad_avg(1, &cost), cost->ext_flag |= KANN_F_COST;
+ roots[n_roots - 1] = cost = kad_avg(1, &cost), cost->ext_flag |= KANN_F_COST;
free(a->v);
a->v = kad_compile_array(&a->n, n_roots, roots);
}
@@ -96,7 +114,7 @@ kann_t *kann_new(kad_node_t *cost, int n_rest, ...)
kann_t *kann_clone(kann_t *a, int batch_size)
{
kann_t *b;
- b = (kann_t*)calloc(1, sizeof(kann_t));
+ b = (kann_t *) calloc(1, sizeof(kann_t));
b->n = a->n;
b->v = kad_clone(a->n, a->v, batch_size);
kad_ext_collate(b->n, b->v, &b->x, &b->g, &b->c);
@@ -106,7 +124,7 @@ kann_t *kann_clone(kann_t *a, int batch_size)
kann_t *kann_unroll_array(kann_t *a, int *len)
{
kann_t *b;
- b = (kann_t*)calloc(1, sizeof(kann_t));
+ b = (kann_t *) calloc(1, sizeof(kann_t));
b->x = a->x, b->g = a->g, b->c = a->c; /* these arrays are shared */
b->v = kad_unroll(a->n, a->v, &b->n, len);
return b;
@@ -118,7 +136,7 @@ kann_t *kann_unroll(kann_t *a, ...)
va_list ap;
int i, n_pivots, *len;
n_pivots = kad_n_pivots(a->n, a->v);
- len = (int*)calloc(n_pivots, sizeof(int));
+ len = (int *) calloc(n_pivots, sizeof(int));
va_start(ap, a);
for (i = 0; i < n_pivots; ++i) len[i] = va_arg(ap, int);
va_end(ap);
@@ -137,7 +155,9 @@ void kann_delete_unrolled(kann_t *a)
void kann_delete(kann_t *a)
{
if (a == 0) return;
- free(a->x); free(a->g); free(a->c);
+ free(a->x);
+ free(a->g);
+ free(a->c);
kann_delete_unrolled(a);
}
@@ -146,7 +166,7 @@ static void kann_switch_core(kann_t *a, int is_train)
int i;
for (i = 0; i < a->n; ++i)
if (a->v[i]->op == 12 && a->v[i]->n_child == 2)
- *(int32_t*)a->v[i]->ptr = !!is_train;
+ *(int32_t *) a->v[i]->ptr = !!is_train;
}
#define chk_flg(flag, mask) ((mask) == 0 || ((flag) & (mask)))
@@ -158,7 +178,8 @@ int kann_find(const kann_t *a, uint32_t ext_flag, int32_t ext_label)
for (i = k = 0; i < a->n; ++i)
if (chk_flg(a->v[i]->ext_flag, ext_flag) && chk_lbl(a->v[i]->ext_label, ext_label))
++k, r = i;
- return k == 1? r : k == 0? -1 : -2;
+ return k == 1 ? r : k == 0 ? -1
+ : -2;
}
int kann_feed_bind(kann_t *a, uint32_t ext_flag, int32_t ext_label, float **x)
@@ -176,8 +197,10 @@ int kann_feed_dim(const kann_t *a, uint32_t ext_flag, int32_t ext_label)
int i, k, n = 0;
for (i = k = 0; i < a->n; ++i)
if (kad_is_feed(a->v[i]) && chk_flg(a->v[i]->ext_flag, ext_flag) && chk_lbl(a->v[i]->ext_label, ext_label))
- ++k, n = a->v[i]->n_d > 1? kad_len(a->v[i]) / a->v[i]->d[0] : a->v[i]->n_d == 1? a->v[i]->d[0] : 1;
- return k == 1? n : k == 0? -1 : -2;
+ ++k, n = a->v[i]->n_d > 1 ? kad_len(a->v[i]) / a->v[i]->d[0] : a->v[i]->n_d == 1 ? a->v[i]->d[0]
+ : 1;
+ return k == 1 ? n : k == 0 ? -1
+ : -2;
}
static float kann_cost_core(kann_t *a, int cost_label, int cal_grad)
@@ -210,7 +233,8 @@ void kann_rnn_start(kann_t *a)
if (p->pre) { /* NB: BE CAREFUL of the interaction between kann_rnn_start() and kann_set_batch_size() */
kad_node_t *q = p->pre;
if (q->x) memcpy(p->x, q->x, kad_len(p) * sizeof(float));
- else memset(p->x, 0, kad_len(p) * sizeof(float));
+ else
+ memset(p->x, 0, kad_len(p) * sizeof(float));
if (q->n_child > 0) free(q->x);
q->x = p->x;
}
@@ -223,7 +247,7 @@ void kann_rnn_end(kann_t *a)
kad_ext_sync(a->n, a->v, a->x, a->g, a->c);
for (i = 0; i < a->n; ++i)
if (a->v[i]->pre && a->v[i]->pre->n_child > 0)
- a->v[i]->pre->x = (float*)calloc(kad_len(a->v[i]->pre), sizeof(float));
+ a->v[i]->pre->x = (float *) calloc(kad_len(a->v[i]->pre), sizeof(float));
}
static int kann_class_error_core(const kann_t *ann, int *base)
@@ -238,10 +262,10 @@ static int kann_class_error_core(const kann_t *ann, int *base)
float t_sum = 0.0f, t_min = 1.0f, t_max = 0.0f, x_max = 0.0f, x_min = 1.0f;
int x_max_k = -1, t_max_k = -1;
for (k = 0; k < n; ++k) {
- float xk = x->x[off+k], tk = t->x[off+k];
+ float xk = x->x[off + k], tk = t->x[off + k];
t_sum += tk;
- t_min = t_min < tk? t_min : tk;
- x_min = x_min < xk? x_min : xk;
+ t_min = t_min < tk ? t_min : tk;
+ x_min = x_min < xk ? x_min : xk;
if (t_max < tk) t_max = tk, t_max_k = k;
if (x_max < xk) x_max = xk, x_max_k = k;
}
@@ -283,7 +307,7 @@ typedef struct mtaux_t { /* cross-worker data */
static void *mt_worker(void *data) /* pthread worker */
{
- mtaux1_t *mt1 = (mtaux1_t*)data;
+ mtaux1_t *mt1 = (mtaux1_t *) data;
mtaux_t *mt = mt1->g;
for (;;) {
int action;
@@ -297,7 +321,8 @@ static void *mt_worker(void *data) /* pthread worker */
if (action == -1) break;
if (mt->eval_out) kann_eval(mt1->a, KANN_F_OUT, 0);
- else mt1->cost = kann_cost_core(mt1->a, mt->cost_label, mt->cal_grad);
+ else
+ mt1->cost = kann_cost_core(mt1->a, mt->cost_label, mt->cal_grad);
}
pthread_exit(0);
}
@@ -324,18 +349,18 @@ void kann_mt(kann_t *ann, int n_threads, int max_batch_size)
int i, k;
if (n_threads <= 1) {
- if (ann->mt) mt_destroy((mtaux_t*)ann->mt);
+ if (ann->mt) mt_destroy((mtaux_t *) ann->mt);
ann->mt = 0;
return;
}
if (n_threads > max_batch_size) n_threads = max_batch_size;
if (n_threads <= 1) return;
- mt = (mtaux_t*)calloc(1, sizeof(mtaux_t));
+ mt = (mtaux_t *) calloc(1, sizeof(mtaux_t));
mt->n_threads = n_threads, mt->max_batch_size = max_batch_size;
pthread_mutex_init(&mt->mtx, 0);
pthread_cond_init(&mt->cv, 0);
- mt->mt = (mtaux1_t*)calloc(n_threads, sizeof(mtaux1_t));
+ mt->mt = (mtaux1_t *) calloc(n_threads, sizeof(mtaux1_t));
for (i = k = 0; i < n_threads; ++i) {
int size = (max_batch_size - k) / (n_threads - i);
mt->mt[i].a = kann_clone(ann, size);
@@ -350,11 +375,11 @@ void kann_mt(kann_t *ann, int n_threads, int max_batch_size)
static void mt_kickoff(kann_t *a, int cost_label, int cal_grad, int eval_out)
{
- mtaux_t *mt = (mtaux_t*)a->mt;
+ mtaux_t *mt = (mtaux_t *) a->mt;
int i, j, k, B, n_var;
B = kad_sync_dim(a->n, a->v, -1); /* get the current batch size */
- assert(B <= mt->max_batch_size); /* TODO: can be relaxed */
+ assert(B <= mt->max_batch_size); /* TODO: can be relaxed */
n_var = kann_size_var(a);
pthread_mutex_lock(&mt->mtx);
@@ -376,7 +401,7 @@ static void mt_kickoff(kann_t *a, int cost_label, int cal_grad, int eval_out)
float kann_cost(kann_t *a, int cost_label, int cal_grad)
{
- mtaux_t *mt = (mtaux_t*)a->mt;
+ mtaux_t *mt = (mtaux_t *) a->mt;
int i, j, B, k, n_var;
float cost;
@@ -392,7 +417,7 @@ float kann_cost(kann_t *a, int cost_label, int cal_grad)
for (i = k = 0, cost = 0.0f; i < mt->n_threads; ++i) {
int size = (B - k) / (mt->n_threads - i);
cost += mt->mt[i].cost * size / B;
- kad_saxpy(n_var, (float)size / B, mt->mt[i].a->g, a->g);
+ kad_saxpy(n_var, (float) size / B, mt->mt[i].a->g, a->g);
k += size;
}
for (j = 0; j < a->n; ++j) { /* copy values back at recurrent nodes (needed by textgen; TODO: temporary solution) */
@@ -410,14 +435,14 @@ float kann_cost(kann_t *a, int cost_label, int cal_grad)
int kann_eval_out(kann_t *a)
{
- mtaux_t *mt = (mtaux_t*)a->mt;
+ mtaux_t *mt = (mtaux_t *) a->mt;
int j, B, n_eval;
if (mt == 0) return kann_eval(a, KANN_F_OUT, 0);
B = kad_sync_dim(a->n, a->v, -1); /* get the current batch size */
mt_kickoff(a, 0, 0, 1);
n_eval = kann_eval(mt->mt[0].a, KANN_F_OUT, 0);
while (mt->n_idle < mt->n_threads - 1); /* busy waiting until all threads in sync */
- for (j = 0; j < a->n; ++j) { /* copy output values back */
+ for (j = 0; j < a->n; ++j) { /* copy output values back */
kad_node_t *p = a->v[j];
if (p->ext_flag & KANN_F_OUT) {
int i, t, k, d0 = p->d[0] / B, d1 = 1; /* for RNN, p->d[0] may equal unroll_len * batch_size */
@@ -438,7 +463,7 @@ int kann_eval_out(kann_t *a)
int kann_class_error(const kann_t *ann, int *base)
{
- mtaux_t *mt = (mtaux_t*)ann->mt;
+ mtaux_t *mt = (mtaux_t *) ann->mt;
int i, n_err = 0, b = 0;
if (mt == 0) return kann_class_error_core(ann, base);
for (i = 0; i < mt->n_threads; ++i) {
@@ -450,7 +475,7 @@ int kann_class_error(const kann_t *ann, int *base)
void kann_switch(kann_t *ann, int is_train)
{
- mtaux_t *mt = (mtaux_t*)ann->mt;
+ mtaux_t *mt = (mtaux_t *) ann->mt;
int i;
if (mt == 0) {
kann_switch_core(ann, is_train);
@@ -460,11 +485,25 @@ void kann_switch(kann_t *ann, int is_train)
kann_switch_core(mt->mt[i].a, is_train);
}
#else
-void kann_mt(kann_t *ann, int n_threads, int max_batch_size) {}
-float kann_cost(kann_t *a, int cost_label, int cal_grad) { return kann_cost_core(a, cost_label, cal_grad); }
-int kann_eval_out(kann_t *a) { return kann_eval(a, KANN_F_OUT, 0); }
-int kann_class_error(const kann_t *a, int *base) { return kann_class_error_core(a, base); }
-void kann_switch(kann_t *ann, int is_train) { return kann_switch_core(ann, is_train); }
+void kann_mt(kann_t *ann, int n_threads, int max_batch_size)
+{
+}
+float kann_cost(kann_t *a, int cost_label, int cal_grad)
+{
+ return kann_cost_core(a, cost_label, cal_grad);
+}
+int kann_eval_out(kann_t *a)
+{
+ return kann_eval(a, KANN_F_OUT, 0);
+}
+int kann_class_error(const kann_t *a, int *base)
+{
+ return kann_class_error_core(a, base);
+}
+void kann_switch(kann_t *ann, int is_train)
+{
+ return kann_switch_core(ann, is_train);
+}
#endif
/***********************
@@ -485,7 +524,7 @@ void kann_save_fp(FILE *fp, kann_t *ann)
void kann_save(const char *fn, kann_t *ann)
{
FILE *fp;
- fp = fn && strcmp(fn, "-")? fopen(fn, "wb") : stdout;
+ fp = fn && strcmp(fn, "-") ? fopen(fn, "wb") : stdout;
kann_save_fp(fp, ann);
fclose(fp);
}
@@ -500,13 +539,13 @@ kann_t *kann_load_fp(FILE *fp)
if (strncmp(magic, KANN_MAGIC, 4) != 0) {
return 0;
}
- ann = (kann_t*)calloc(1, sizeof(kann_t));
+ ann = (kann_t *) calloc(1, sizeof(kann_t));
ann->v = kad_load(fp, &ann->n);
n_var = kad_size_var(ann->n, ann->v);
n_const = kad_size_const(ann->n, ann->v);
- ann->x = (float*)malloc(n_var * sizeof(float));
- ann->g = (float*)calloc(n_var, sizeof(float));
- ann->c = (float*)malloc(n_const * sizeof(float));
+ ann->x = (float *) malloc(n_var * sizeof(float));
+ ann->g = (float *) calloc(n_var, sizeof(float));
+ ann->c = (float *) malloc(n_const * sizeof(float));
(void) !fread(ann->x, sizeof(float), n_var, fp);
(void) !fread(ann->c, sizeof(float), n_const, fp);
kad_ext_sync(ann->n, ann->v, ann->x, ann->g, ann->c);
@@ -517,7 +556,7 @@ kann_t *kann_load(const char *fn)
{
FILE *fp;
kann_t *ann;
- fp = fn && strcmp(fn, "-")? fopen(fn, "rb") : stdin;
+ fp = fn && strcmp(fn, "-") ? fopen(fn, "rb") : stdin;
ann = kann_load_fp(fp);
fclose(fp);
return ann;
@@ -531,23 +570,24 @@ kann_t *kann_load(const char *fn)
kad_node_t *kann_new_leaf_array(int *offset, kad_node_p *par, uint8_t flag, float x0_01, int n_d, int32_t d[KAD_MAX_DIM])
{
- int i, len, off = offset && par? *offset : -1;
+ int i, len, off = offset && par ? *offset : -1;
kad_node_t *p;
if (off >= 0 && par[off]) return par[(*offset)++];
- p = (kad_node_t*)calloc(1, sizeof(kad_node_t));
+ p = (kad_node_t *) calloc(1, sizeof(kad_node_t));
p->n_d = n_d, p->flag = flag;
memcpy(p->d, d, n_d * sizeof(int32_t));
len = kad_len(p);
- p->x = (float*)calloc(len, sizeof(float));
+ p->x = (float *) calloc(len, sizeof(float));
if (p->n_d <= 1) {
for (i = 0; i < len; ++i)
p->x[i] = x0_01;
- } else {
+ }
+ else {
double sdev_inv;
- sdev_inv = 1.0 / sqrt((double)len / p->d[0]);
+ sdev_inv = 1.0 / sqrt((double) len / p->d[0]);
for (i = 0; i < len; ++i)
- p->x[i] = (float)(kad_drand_normal(0) * sdev_inv);
+ p->x[i] = (float) (kad_drand_normal(0) * sdev_inv);
}
if (off >= 0) par[off] = p, ++(*offset);
return p;
@@ -557,7 +597,9 @@ kad_node_t *kann_new_leaf2(int *offset, kad_node_p *par, uint8_t flag, float x0_
{
int32_t i, d[KAD_MAX_DIM];
va_list ap;
- va_start(ap, n_d); for (i = 0; i < n_d; ++i) d[i] = va_arg(ap, int); va_end(ap);
+ va_start(ap, n_d);
+ for (i = 0; i < n_d; ++i) d[i] = va_arg(ap, int);
+ va_end(ap);
return kann_new_leaf_array(offset, par, flag, x0_01, n_d, d);
}
@@ -565,7 +607,7 @@ kad_node_t *kann_layer_dense2(int *offset, kad_node_p *par, kad_node_t *in, int
{
int n0;
kad_node_t *w, *b;
- n0 = in->n_d >= 2? kad_len(in) / in->d[0] : kad_len(in);
+ n0 = in->n_d >= 2 ? kad_len(in) / in->d[0] : kad_len(in);
w = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 2, n1, n0);
b = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 1, n1);
return kad_add(kad_cmul(in, w), b);
@@ -583,27 +625,27 @@ kad_node_t *kann_layer_layernorm2(int *offset, kad_node_t **par, kad_node_t *in)
{
int n0;
kad_node_t *alpha, *beta;
- n0 = in->n_d >= 2? kad_len(in) / in->d[0] : kad_len(in);
+ n0 = in->n_d >= 2 ? kad_len(in) / in->d[0] : kad_len(in);
alpha = kann_new_leaf2(offset, par, KAD_VAR, 1.0f, 1, n0);
- beta = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 1, n0);
+ beta = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 1, n0);
return kad_add(kad_mul(kad_stdnorm(in), alpha), beta);
}
static inline kad_node_t *cmul_norm2(int *offset, kad_node_t **par, kad_node_t *x, kad_node_t *w, int use_norm)
{
- return use_norm? kann_layer_layernorm2(offset, par, kad_cmul(x, w)) : kad_cmul(x, w);
+ return use_norm ? kann_layer_layernorm2(offset, par, kad_cmul(x, w)) : kad_cmul(x, w);
}
kad_node_t *kann_layer_rnn2(int *offset, kad_node_t **par, kad_node_t *in, kad_node_t *h0, int rnn_flag)
{
- int n0, n1 = h0->d[h0->n_d-1], use_norm = !!(rnn_flag & KANN_RNN_NORM);
+ int n0, n1 = h0->d[h0->n_d - 1], use_norm = !!(rnn_flag & KANN_RNN_NORM);
kad_node_t *t, *w, *u, *b, *out;
u = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 2, n1, n1);
b = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 1, n1);
t = cmul_norm2(offset, par, h0, u, use_norm);
if (in) {
- n0 = in->n_d >= 2? kad_len(in) / in->d[0] : kad_len(in);
+ n0 = in->n_d >= 2 ? kad_len(in) / in->d[0] : kad_len(in);
w = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 2, n1, n0);
t = kad_add(cmul_norm2(offset, par, in, w, use_norm), t);
}
@@ -614,10 +656,10 @@ kad_node_t *kann_layer_rnn2(int *offset, kad_node_t **par, kad_node_t *in, kad_n
kad_node_t *kann_layer_gru2(int *offset, kad_node_t **par, kad_node_t *in, kad_node_t *h0, int rnn_flag)
{
- int n0 = 0, n1 = h0->d[h0->n_d-1], use_norm = !!(rnn_flag & KANN_RNN_NORM);
+ int n0 = 0, n1 = h0->d[h0->n_d - 1], use_norm = !!(rnn_flag & KANN_RNN_NORM);
kad_node_t *t, *r, *z, *w, *u, *b, *s, *out;
- if (in) n0 = in->n_d >= 2? kad_len(in) / in->d[0] : kad_len(in);
+ if (in) n0 = in->n_d >= 2 ? kad_len(in) / in->d[0] : kad_len(in);
/* z = sigm(x_t * W_z + h_{t-1} * U_z + b_z) */
u = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 2, n1, n1);
b = kann_new_leaf2(offset, par, KAD_VAR, 0.0f, 1, n1);
@@ -657,16 +699,36 @@ kad_node_t *kann_new_leaf(uint8_t flag, float x0_01, int n_d, ...)
{
int32_t i, d[KAD_MAX_DIM];
va_list ap;
- va_start(ap, n_d); for (i = 0; i < n_d; ++i) d[i] = va_arg(ap, int); va_end(ap);
+ va_start(ap, n_d);
+ for (i = 0; i < n_d; ++i) d[i] = va_arg(ap, int);
+ va_end(ap);
return kann_new_leaf_array(0, 0, flag, x0_01, n_d, d);
}
-kad_node_t *kann_new_scalar(uint8_t flag, float x) { return kann_new_leaf(flag, x, 0); }
-kad_node_t *kann_new_weight(int n_row, int n_col) { return kann_new_leaf(KAD_VAR, 0.0f, 2, n_row, n_col); }
-kad_node_t *kann_new_vec(int n, float x) { return kann_new_leaf(KAD_VAR, x, 1, n); }
-kad_node_t *kann_new_bias(int n) { return kann_new_vec(n, 0.0f); }
-kad_node_t *kann_new_weight_conv2d(int n_out, int n_in, int k_row, int k_col) { return kann_new_leaf(KAD_VAR, 0.0f, 4, n_out, n_in, k_row, k_col); }
-kad_node_t *kann_new_weight_conv1d(int n_out, int n_in, int kernel_len) { return kann_new_leaf(KAD_VAR, 0.0f, 3, n_out, n_in, kernel_len); }
+kad_node_t *kann_new_scalar(uint8_t flag, float x)
+{
+ return kann_new_leaf(flag, x, 0);
+}
+kad_node_t *kann_new_weight(int n_row, int n_col)
+{
+ return kann_new_leaf(KAD_VAR, 0.0f, 2, n_row, n_col);
+}
+kad_node_t *kann_new_vec(int n, float x)
+{
+ return kann_new_leaf(KAD_VAR, x, 1, n);
+}
+kad_node_t *kann_new_bias(int n)
+{
+ return kann_new_vec(n, 0.0f);
+}
+kad_node_t *kann_new_weight_conv2d(int n_out, int n_in, int k_row, int k_col)
+{
+ return kann_new_leaf(KAD_VAR, 0.0f, 4, n_out, n_in, k_row, k_col);
+}
+kad_node_t *kann_new_weight_conv1d(int n_out, int n_in, int kernel_len)
+{
+ return kann_new_leaf(KAD_VAR, 0.0f, 3, n_out, n_in, kernel_len);
+}
kad_node_t *kann_layer_input(int n1)
{
@@ -676,23 +738,32 @@ kad_node_t *kann_layer_input(int n1)
return t;
}
-kad_node_t *kann_layer_dense(kad_node_t *in, int n1) { return kann_layer_dense2(0, 0, in, n1); }
-kad_node_t *kann_layer_dropout(kad_node_t *t, float r) { return kann_layer_dropout2(0, 0, t, r); }
-kad_node_t *kann_layer_layernorm(kad_node_t *in) { return kann_layer_layernorm2(0, 0, in); }
+kad_node_t *kann_layer_dense(kad_node_t *in, int n1)
+{
+ return kann_layer_dense2(0, 0, in, n1);
+}
+kad_node_t *kann_layer_dropout(kad_node_t *t, float r)
+{
+ return kann_layer_dropout2(0, 0, t, r);
+}
+kad_node_t *kann_layer_layernorm(kad_node_t *in)
+{
+ return kann_layer_layernorm2(0, 0, in);
+}
kad_node_t *kann_layer_rnn(kad_node_t *in, int n1, int rnn_flag)
{
kad_node_t *h0;
- h0 = (rnn_flag & KANN_RNN_VAR_H0)? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
- h0->x = (float*)calloc(n1, sizeof(float));
+ h0 = (rnn_flag & KANN_RNN_VAR_H0) ? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
+ h0->x = (float *) calloc(n1, sizeof(float));
return kann_layer_rnn2(0, 0, in, h0, rnn_flag);
}
kad_node_t *kann_layer_gru(kad_node_t *in, int n1, int rnn_flag)
{
kad_node_t *h0;
- h0 = (rnn_flag & KANN_RNN_VAR_H0)? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
- h0->x = (float*)calloc(n1, sizeof(float));
+ h0 = (rnn_flag & KANN_RNN_VAR_H0) ? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
+ h0->x = (float *) calloc(n1, sizeof(float));
return kann_layer_gru2(0, 0, in, h0, rnn_flag);
}
@@ -705,13 +776,13 @@ kad_node_t *kann_layer_lstm(kad_node_t *in, int n1, int rnn_flag)
{
int n0;
kad_node_t *i, *f, *o, *g, *w, *u, *b, *h0, *c0, *c, *out;
- kad_node_t *(*cmul)(kad_node_t*, kad_node_t*) = (rnn_flag & KANN_RNN_NORM)? kann_cmul_norm : kad_cmul;
+ kad_node_t *(*cmul)(kad_node_t *, kad_node_t *) = (rnn_flag & KANN_RNN_NORM) ? kann_cmul_norm : kad_cmul;
- n0 = in->n_d >= 2? kad_len(in) / in->d[0] : kad_len(in);
- h0 = (rnn_flag & KANN_RNN_VAR_H0)? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
- h0->x = (float*)calloc(n1, sizeof(float));
- c0 = (rnn_flag & KANN_RNN_VAR_H0)? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
- c0->x = (float*)calloc(n1, sizeof(float));
+ n0 = in->n_d >= 2 ? kad_len(in) / in->d[0] : kad_len(in);
+ h0 = (rnn_flag & KANN_RNN_VAR_H0) ? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
+ h0->x = (float *) calloc(n1, sizeof(float));
+ c0 = (rnn_flag & KANN_RNN_VAR_H0) ? kad_var(0, 0, 2, 1, n1) : kad_const(0, 2, 1, n1);
+ c0->x = (float *) calloc(n1, sizeof(float));
/* i = sigm(x_t * W_i + h_{t-1} * U_i + b_i) */
w = kann_new_weight(n1, n0);
@@ -766,18 +837,21 @@ kad_node_t *kann_layer_cost(kad_node_t *t, int n_out, int cost_type)
if (cost_type == KANN_C_MSE) {
cost = kad_mse(t, truth);
- } else if (cost_type == KANN_C_CEB) {
+ }
+ else if (cost_type == KANN_C_CEB) {
t = kad_sigm(t);
cost = kad_ce_bin(t, truth);
- } else if (cost_type == KANN_C_CEB_NEG) {
+ }
+ else if (cost_type == KANN_C_CEB_NEG) {
t = kad_tanh(t);
cost = kad_ce_bin_neg(t, truth);
- } else if (cost_type == KANN_C_CEM) {
+ }
+ else if (cost_type == KANN_C_CEM) {
t = kad_softmax(t);
cost = kad_ce_multi(t, truth);
}
else {
- assert (0);
+ assert(0);
}
t->ext_flag |= KANN_F_OUT;
@@ -791,8 +865,8 @@ void kann_shuffle(int n, int *s)
int i, j, t;
for (i = 0; i < n; ++i) s[i] = i;
for (i = n; i > 0; --i) {
- j = (int)(i * kad_drand(0));
- t = s[j], s[j] = s[i-1], s[i-1] = t;
+ j = (int) (i * kad_drand(0));
+ t = s[j], s[j] = s[i - 1], s[i - 1] = t;
}
}
@@ -805,7 +879,7 @@ void kann_shuffle(int n, int *s)
void kann_RMSprop(int n, float h0, const float *h, float decay, const float *g, float *t, float *r)
{
- int i, n4 = n>>2<<2;
+ int i, n4 = n >> 2 << 2;
__m128 vh, vg, vr, vt, vd, vd1, tmp, vtiny;
vh = _mm_set1_ps(h0);
vd = _mm_set1_ps(decay);
@@ -823,7 +897,7 @@ void kann_RMSprop(int n, float h0, const float *h, float decay, const float *g,
}
for (; i < n; ++i) {
r[i] = (1. - decay) * g[i] * g[i] + decay * r[i];
- t[i] -= (h? h[i] : h0) / sqrtf(1e-6f + r[i]) * g[i];
+ t[i] -= (h ? h[i] : h0) / sqrtf(1e-6f + r[i]) * g[i];
}
}
#else
@@ -831,7 +905,7 @@ void kann_RMSprop(int n, float h0, const float *h, float decay, const float *g,
{
int i;
for (i = 0; i < n; ++i) {
- float lr = h? h[i] : h0;
+ float lr = h ? h[i] : h0;
r[i] = (1.0f - decay) * g[i] * g[i] + decay * r[i];
t[i] -= lr / sqrtf(1e-6f + r[i]) * g[i];
}
@@ -847,8 +921,8 @@ float kann_grad_clip(float thres, int n, float *g)
s2 = sqrt(s2);
if (s2 > thres)
for (i = 0, s2 = 1.0 / s2; i < n; ++i)
- g[i] *= (float)s2;
- return (float)s2 / thres;
+ g[i] *= (float) s2;
+ return (float) s2 / thres;
}
/****************************************************************
@@ -868,21 +942,21 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
if (n_in < 0 || n_out < 0) return -1;
n_var = kann_size_var(ann);
n_const = kann_size_const(ann);
- r = (float*)calloc(n_var, sizeof(float));
- shuf = (int*)malloc(n * sizeof(int));
- x = (float**)malloc(n * sizeof(float*));
- y = (float**)malloc(n * sizeof(float*));
+ r = (float *) calloc(n_var, sizeof(float));
+ shuf = (int *) malloc(n * sizeof(int));
+ x = (float **) malloc(n * sizeof(float *));
+ y = (float **) malloc(n * sizeof(float *));
kann_shuffle(n, shuf);
for (j = 0; j < n; ++j)
x[j] = _x[shuf[j]], y[j] = _y[shuf[j]];
- n_val = (int)(n * frac_val);
+ n_val = (int) (n * frac_val);
n_train = n - n_val;
- min_x = (float*)malloc(n_var * sizeof(float));
- min_c = (float*)malloc(n_const * sizeof(float));
+ min_x = (float *) malloc(n_var * sizeof(float));
+ min_c = (float *) malloc(n_const * sizeof(float));
- x1 = (float*)malloc(n_in * mini_size * sizeof(float));
- y1 = (float*)malloc(n_out * mini_size * sizeof(float));
- kann_feed_bind(ann, KANN_F_IN, 0, &x1);
+ x1 = (float *) malloc(n_in * mini_size * sizeof(float));
+ y1 = (float *) malloc(n_out * mini_size * sizeof(float));
+ kann_feed_bind(ann, KANN_F_IN, 0, &x1);
kann_feed_bind(ann, KANN_F_TRUTH, 0, &y1);
for (i = 0; i < max_epoch; ++i) {
@@ -891,10 +965,10 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
kann_shuffle(n_train, shuf);
kann_switch(ann, 1);
while (n_proc < n_train) {
- int b, c, ms = n_train - n_proc < mini_size? n_train - n_proc : mini_size;
+ int b, c, ms = n_train - n_proc < mini_size ? n_train - n_proc : mini_size;
for (b = 0; b < ms; ++b) {
- memcpy(&x1[b*n_in], x[shuf[n_proc+b]], n_in * sizeof(float));
- memcpy(&y1[b*n_out], y[shuf[n_proc+b]], n_out * sizeof(float));
+ memcpy(&x1[b * n_in], x[shuf[n_proc + b]], n_in * sizeof(float));
+ memcpy(&y1[b * n_out], y[shuf[n_proc + b]], n_out * sizeof(float));
}
kann_set_batch_size(ann, ms);
train_cost += kann_cost(ann, 0, 1) * ms;
@@ -907,10 +981,10 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
kann_switch(ann, 0);
n_proc = 0;
while (n_proc < n_val) {
- int b, c, ms = n_val - n_proc < mini_size? n_val - n_proc : mini_size;
+ int b, c, ms = n_val - n_proc < mini_size ? n_val - n_proc : mini_size;
for (b = 0; b < ms; ++b) {
- memcpy(&x1[b*n_in], x[n_train+n_proc+b], n_in * sizeof(float));
- memcpy(&y1[b*n_out], y[n_train+n_proc+b], n_out * sizeof(float));
+ memcpy(&x1[b * n_in], x[n_train + n_proc + b], n_in * sizeof(float));
+ memcpy(&y1[b * n_out], y[n_train + n_proc + b], n_out * sizeof(float));
}
kann_set_batch_size(ann, ms);
val_cost += kann_cost(ann, 0, 0) * ms;
@@ -919,6 +993,8 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
n_proc += ms;
}
if (n_val > 0) val_cost /= n_val;
+ (void) (n_train_err);
+ (void) (n_val_err);
if (cb) {
cb(i + 1, train_cost, val_cost, ud);
#if 0
@@ -937,8 +1013,9 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
memcpy(min_x, ann->x, n_var * sizeof(float));
memcpy(min_c, ann->c, n_const * sizeof(float));
drop_streak = 0;
- min_val_cost = (float)val_cost;
- } else if (++drop_streak >= max_drop_streak)
+ min_val_cost = (float) val_cost;
+ }
+ else if (++drop_streak >= max_drop_streak)
break;
}
}
@@ -947,13 +1024,20 @@ int kann_train_fnn1(kann_t *ann, float lr, int mini_size, int max_epoch,
memcpy(ann->c, min_c, n_const * sizeof(float));
}
- free(min_c); free(min_x); free(y1); free(x1); free(y); free(x); free(shuf); free(r);
+ free(min_c);
+ free(min_x);
+ free(y1);
+ free(x1);
+ free(y);
+ free(x);
+ free(shuf);
+ free(r);
return i;
}
float kann_cost_fnn1(kann_t *ann, int n, float **x, float **y)
{
- int n_in, n_out, n_proc = 0, mini_size = 64 < n? 64 : n;
+ int n_in, n_out, n_proc = 0, mini_size = 64 < n ? 64 : n;
float *x1, *y1;
double cost = 0.0;
@@ -961,23 +1045,24 @@ float kann_cost_fnn1(kann_t *ann, int n, float **x, float **y)
n_out = kann_dim_out(ann);
if (n <= 0 || n_in < 0 || n_out < 0) return 0.0;
- x1 = (float*)malloc(n_in * mini_size * sizeof(float));
- y1 = (float*)malloc(n_out * mini_size * sizeof(float));
- kann_feed_bind(ann, KANN_F_IN, 0, &x1);
+ x1 = (float *) malloc(n_in * mini_size * sizeof(float));
+ y1 = (float *) malloc(n_out * mini_size * sizeof(float));
+ kann_feed_bind(ann, KANN_F_IN, 0, &x1);
kann_feed_bind(ann, KANN_F_TRUTH, 0, &y1);
kann_switch(ann, 0);
while (n_proc < n) {
- int b, ms = n - n_proc < mini_size? n - n_proc : mini_size;
+ int b, ms = n - n_proc < mini_size ? n - n_proc : mini_size;
for (b = 0; b < ms; ++b) {
- memcpy(&x1[b*n_in], x[n_proc+b], n_in * sizeof(float));
- memcpy(&y1[b*n_out], y[n_proc+b], n_out * sizeof(float));
+ memcpy(&x1[b * n_in], x[n_proc + b], n_in * sizeof(float));
+ memcpy(&y1[b * n_out], y[n_proc + b], n_out * sizeof(float));
}
kann_set_batch_size(ann, ms);
cost += kann_cost(ann, 0, 0) * ms;
n_proc += ms;
}
- free(y1); free(x1);
- return (float)(cost / n);
+ free(y1);
+ free(x1);
+ return (float) (cost / n);
}
const float *kann_apply1(kann_t *a, float *x)
diff --git a/contrib/librdns/curve.c b/contrib/librdns/curve.c
index 19ec2508c..9fc345fb5 100644
--- a/contrib/librdns/curve.c
+++ b/contrib/librdns/curve.c
@@ -1,4 +1,20 @@
/*
+ * Copyright 2024 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
* Copyright (c) 2014, Vsevolod Stakhov
*
* All rights reserved.
@@ -39,12 +55,11 @@
#include <tweetnacl.h>
-void
-randombytes(uint8_t *data, uint64_t len)
+void randombytes(uint8_t *data, uint64_t len)
{
- ottery_rand_bytes (data, len);
+ ottery_rand_bytes(data, len);
}
-void sodium_memzero (uint8_t *data, uint64_t len)
+void sodium_memzero(uint8_t *data, uint64_t len)
{
volatile uint8_t *p = data;
@@ -54,14 +69,13 @@ void sodium_memzero (uint8_t *data, uint64_t len)
}
void sodium_init(void)
{
-
}
-ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data);
-ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len,
- void *plugin_data, struct rdns_request **req_out);
-void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data);
-void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data);
+ssize_t rdns_curve_send(struct rdns_request *req, void *plugin_data);
+ssize_t rdns_curve_recv(struct rdns_io_channel *ioc, void *buf, size_t len,
+ void *plugin_data, struct rdns_request **req_out);
+void rdns_curve_finish_request(struct rdns_request *req, void *plugin_data);
+void rdns_curve_dtor(struct rdns_resolver *resolver, void *plugin_data);
struct rdns_curve_entry {
char *name;
@@ -103,34 +117,36 @@ struct rdns_curve_ctx {
};
static struct rdns_curve_client_key *
-rdns_curve_client_key_new (struct rdns_curve_ctx *ctx)
+rdns_curve_client_key_new(struct rdns_curve_ctx *ctx)
{
struct rdns_curve_client_key *new;
struct rdns_curve_nm_entry *nm;
struct rdns_curve_entry *entry, *tmp;
- new = calloc (1, sizeof (struct rdns_curve_client_key));
- crypto_box_keypair (new->pk, new->sk);
+ new = calloc(1, sizeof(struct rdns_curve_client_key));
+ crypto_box_keypair(new->pk, new->sk);
- HASH_ITER (hh, ctx->entries, entry, tmp) {
- nm = calloc (1, sizeof (struct rdns_curve_nm_entry));
+ HASH_ITER(hh, ctx->entries, entry, tmp)
+ {
+ nm = calloc(1, sizeof(struct rdns_curve_nm_entry));
nm->entry = entry;
- crypto_box_beforenm (nm->k, entry->pk, new->sk);
+ crypto_box_beforenm(nm->k, entry->pk, new->sk);
- DL_APPEND (new->nms, nm);
+ DL_APPEND(new->nms, nm);
}
- new->counter = ottery_rand_uint64 ();
+ new->counter = ottery_rand_uint64();
return new;
}
static struct rdns_curve_nm_entry *
-rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
+rdns_curve_find_nm(struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
{
struct rdns_curve_nm_entry *nm;
- DL_FOREACH (key->nms, nm) {
+ DL_FOREACH(key->nms, nm)
+ {
if (nm->entry == entry) {
return nm;
}
@@ -140,67 +156,68 @@ rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *
}
static void
-rdns_curve_client_key_free (struct rdns_curve_client_key *key)
+rdns_curve_client_key_free(struct rdns_curve_client_key *key)
{
struct rdns_curve_nm_entry *nm, *tmp;
- DL_FOREACH_SAFE (key->nms, nm, tmp) {
- sodium_memzero (nm->k, sizeof (nm->k));
- free (nm);
+ DL_FOREACH_SAFE(key->nms, nm, tmp)
+ {
+ sodium_memzero(nm->k, sizeof(nm->k));
+ free(nm);
}
- sodium_memzero (key->sk, sizeof (key->sk));
- free (key);
+ sodium_memzero(key->sk, sizeof(key->sk));
+ free(key);
}
-struct rdns_curve_ctx*
-rdns_curve_ctx_new (double key_refresh_interval)
+struct rdns_curve_ctx *
+rdns_curve_ctx_new(double key_refresh_interval)
{
struct rdns_curve_ctx *new;
- new = calloc (1, sizeof (struct rdns_curve_ctx));
+ new = calloc(1, sizeof(struct rdns_curve_ctx));
new->key_refresh_interval = key_refresh_interval;
return new;
}
-void
-rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
- const char *name, const unsigned char *pubkey)
+void rdns_curve_ctx_add_key(struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey)
{
struct rdns_curve_entry *entry;
bool success = true;
- entry = malloc (sizeof (struct rdns_curve_entry));
+ entry = malloc(sizeof(struct rdns_curve_entry));
if (entry != NULL) {
- entry->name = strdup (name);
+ entry->name = strdup(name);
if (entry->name == NULL) {
success = false;
}
- memcpy (entry->pk, pubkey, sizeof (entry->pk));
+ memcpy(entry->pk, pubkey, sizeof(entry->pk));
if (success) {
- HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry);
+ HASH_ADD_KEYPTR(hh, ctx->entries, entry->name, strlen(entry->name), entry);
}
}
}
-#define rdns_curve_write_hex(in, out, offset, base) do { \
- *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \
-} while (0)
+#define rdns_curve_write_hex(in, out, offset, base) \
+ do { \
+ *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \
+ } while (0)
static bool
-rdns_curve_hex_to_byte (const char *in, unsigned char *out)
+rdns_curve_hex_to_byte(const char *in, unsigned char *out)
{
int i;
- for (i = 0; i <= 1; i ++) {
+ for (i = 0; i <= 1; i++) {
if (in[i] >= '0' && in[i] <= '9') {
- rdns_curve_write_hex (in, out, i, '0');
+ rdns_curve_write_hex(in, out, i, '0');
}
else if (in[i] >= 'a' && in[i] <= 'f') {
- rdns_curve_write_hex (in, out, i, 'a' - 10);
+ rdns_curve_write_hex(in, out, i, 'a' - 10);
}
else if (in[i] >= 'A' && in[i] <= 'F') {
- rdns_curve_write_hex (in, out, i, 'A' - 10);
+ rdns_curve_write_hex(in, out, i, 'A' - 10);
}
else {
return false;
@@ -212,16 +229,16 @@ rdns_curve_hex_to_byte (const char *in, unsigned char *out)
#undef rdns_curve_write_hex
unsigned char *
-rdns_curve_key_from_hex (const char *hex)
+rdns_curve_key_from_hex(const char *hex)
{
- unsigned int len = strlen (hex), i;
+ unsigned int len = strlen(hex), i;
unsigned char *res = NULL;
if (len == crypto_box_PUBLICKEYBYTES * 2) {
- res = calloc (1, crypto_box_PUBLICKEYBYTES);
- for (i = 0; i < crypto_box_PUBLICKEYBYTES; i ++) {
- if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) {
- free (res);
+ res = calloc(1, crypto_box_PUBLICKEYBYTES);
+ for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) {
+ if (!rdns_curve_hex_to_byte(&hex[i * 2], &res[i])) {
+ free(res);
return NULL;
}
}
@@ -230,35 +247,34 @@ rdns_curve_key_from_hex (const char *hex)
return res;
}
-void
-rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+void rdns_curve_ctx_destroy(struct rdns_curve_ctx *ctx)
{
struct rdns_curve_entry *entry, *tmp;
- HASH_ITER (hh, ctx->entries, entry, tmp) {
- free (entry->name);
- free (entry);
+ HASH_ITER(hh, ctx->entries, entry, tmp)
+ {
+ free(entry->name);
+ free(entry);
}
- free (ctx);
+ free(ctx);
}
static void
-rdns_curve_refresh_key_callback (void *user_data)
+rdns_curve_refresh_key_callback(void *user_data)
{
struct rdns_curve_ctx *ctx = user_data;
struct rdns_resolver *resolver;
resolver = ctx->resolver;
- rdns_info ("refresh dnscurve keys");
- REF_RELEASE (ctx->cur_key);
- ctx->cur_key = rdns_curve_client_key_new (ctx);
- REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+ rdns_info("refresh dnscurve keys");
+ REF_RELEASE(ctx->cur_key);
+ ctx->cur_key = rdns_curve_client_key_new(ctx);
+ REF_INIT_RETAIN(ctx->cur_key, rdns_curve_client_key_free);
}
-void
-rdns_curve_register_plugin (struct rdns_resolver *resolver,
- struct rdns_curve_ctx *ctx)
+void rdns_curve_register_plugin(struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx)
{
struct rdns_plugin *plugin;
@@ -266,7 +282,7 @@ rdns_curve_register_plugin (struct rdns_resolver *resolver,
return;
}
- plugin = calloc (1, sizeof (struct rdns_plugin));
+ plugin = calloc(1, sizeof(struct rdns_plugin));
if (plugin != NULL) {
plugin->data = ctx;
plugin->type = RDNS_PLUGIN_CURVE;
@@ -274,24 +290,24 @@ rdns_curve_register_plugin (struct rdns_resolver *resolver,
plugin->cb.curve_plugin.recv_cb = rdns_curve_recv;
plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request;
plugin->dtor = rdns_curve_dtor;
- sodium_init ();
- ctx->cur_key = rdns_curve_client_key_new (ctx);
- REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+ sodium_init();
+ ctx->cur_key = rdns_curve_client_key_new(ctx);
+ REF_INIT_RETAIN(ctx->cur_key, rdns_curve_client_key_free);
if (ctx->key_refresh_interval > 0) {
- ctx->key_refresh_event = resolver->async->add_periodic (
- resolver->async->data, ctx->key_refresh_interval,
- rdns_curve_refresh_key_callback, ctx);
+ ctx->key_refresh_event = resolver->async->add_periodic(
+ resolver->async->data, ctx->key_refresh_interval,
+ rdns_curve_refresh_key_callback, ctx);
}
ctx->resolver = resolver;
- rdns_resolver_register_plugin (resolver, plugin);
+ rdns_resolver_register_plugin(resolver, plugin);
}
}
ssize_t
-rdns_curve_send (struct rdns_request *req, void *plugin_data)
+rdns_curve_send(struct rdns_request *req, void *plugin_data)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
struct rdns_curve_entry *entry;
struct iovec iov[4];
unsigned char *m;
@@ -301,62 +317,62 @@ rdns_curve_send (struct rdns_request *req, void *plugin_data)
ssize_t ret, boxed_len;
/* Check for key */
- HASH_FIND_STR (ctx->entries, req->io->srv->name, entry);
+ HASH_FIND_STR(ctx->entries, req->io->srv->name, entry);
if (entry != NULL) {
- nm = rdns_curve_find_nm (ctx->cur_key, entry);
- creq = malloc (sizeof (struct rdns_curve_request));
+ nm = rdns_curve_find_nm(ctx->cur_key, entry);
+ creq = malloc(sizeof(struct rdns_curve_request));
if (creq == NULL) {
return -1;
}
boxed_len = req->pos + crypto_box_ZEROBYTES;
- m = malloc (boxed_len);
+ m = malloc(boxed_len);
if (m == NULL) {
return -1;
}
/* Ottery is faster than sodium native PRG that uses /dev/random only */
- memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t));
- ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t));
- sodium_memzero (creq->nonce + 12, crypto_box_NONCEBYTES - 12);
+ memcpy(creq->nonce, &ctx->cur_key->counter, sizeof(uint64_t));
+ ottery_rand_bytes(creq->nonce + sizeof(uint64_t), 12 - sizeof(uint64_t));
+ sodium_memzero(creq->nonce + 12, crypto_box_NONCEBYTES - 12);
- sodium_memzero (m, crypto_box_ZEROBYTES);
- memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos);
+ sodium_memzero(m, crypto_box_ZEROBYTES);
+ memcpy(m + crypto_box_ZEROBYTES, req->packet, req->pos);
- if (crypto_box_afternm (m, m, boxed_len,
- creq->nonce, nm->k) == -1) {
- sodium_memzero (m, boxed_len);
- free (m);
+ if (crypto_box_afternm(m, m, boxed_len,
+ creq->nonce, nm->k) == -1) {
+ sodium_memzero(m, boxed_len);
+ free(m);
return -1;
}
creq->key = ctx->cur_key;
- REF_RETAIN (ctx->cur_key);
+ REF_RETAIN(ctx->cur_key);
creq->entry = entry;
creq->req = req;
creq->nm = nm;
- HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq);
+ HASH_ADD_KEYPTR(hh, ctx->requests, creq->nonce, 12, creq);
req->curve_plugin_data = creq;
- ctx->cur_key->counter ++;
- ctx->cur_key->uses ++;
+ ctx->cur_key->counter++;
+ ctx->cur_key->uses++;
/* Now form a dnscurve packet */
- iov[0].iov_base = (void *)qmagic;
- iov[0].iov_len = sizeof (qmagic) - 1;
+ iov[0].iov_base = (void *) qmagic;
+ iov[0].iov_len = sizeof(qmagic) - 1;
iov[1].iov_base = ctx->cur_key->pk;
- iov[1].iov_len = sizeof (ctx->cur_key->pk);
+ iov[1].iov_len = sizeof(ctx->cur_key->pk);
iov[2].iov_base = creq->nonce;
iov[2].iov_len = 12;
iov[3].iov_base = m + crypto_box_BOXZEROBYTES;
iov[3].iov_len = boxed_len - crypto_box_BOXZEROBYTES;
- ret = writev (req->io->sock, iov, sizeof (iov) / sizeof (iov[0]));
- sodium_memzero (m, boxed_len);
- free (m);
+ ret = writev(req->io->sock, iov, sizeof(iov) / sizeof(iov[0]));
+ sodium_memzero(m, boxed_len);
+ free(m);
}
else {
- ret = write (req->io->sock, req->packet, req->pos);
+ ret = write(req->io->sock, req->packet, req->pos);
req->curve_plugin_data = NULL;
}
@@ -364,10 +380,10 @@ rdns_curve_send (struct rdns_request *req, void *plugin_data)
}
ssize_t
-rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
- struct rdns_request **req_out)
+rdns_curve_recv(struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
+ struct rdns_request **req_out)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
ssize_t ret, boxlen;
static const char rmagic[] = "R6fnvWJ8";
unsigned char *p, *box;
@@ -376,71 +392,69 @@ rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugi
struct rdns_resolver *resolver;
resolver = ctx->resolver;
- ret = read (ioc->sock, buf, len);
+ ret = read(ioc->sock, buf, len);
if (ret <= 0 || ret < 64) {
/* Definitely not a DNSCurve packet */
return ret;
}
- if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) {
+ if (memcmp(buf, rmagic, sizeof(rmagic) - 1) == 0) {
/* Likely DNSCurve packet */
- p = ((unsigned char *)buf) + 8;
- HASH_FIND (hh, ctx->requests, p, 12, creq);
+ p = ((unsigned char *) buf) + 8;
+ HASH_FIND(hh, ctx->requests, p, 12, creq);
if (creq == NULL) {
- rdns_info ("unable to find nonce in the internal hash");
+ rdns_info("unable to find nonce in the internal hash");
return ret;
}
- memcpy (enonce, p, crypto_box_NONCEBYTES);
+ memcpy(enonce, p, crypto_box_NONCEBYTES);
p += crypto_box_NONCEBYTES;
boxlen = ret - crypto_box_NONCEBYTES +
- crypto_box_BOXZEROBYTES -
- sizeof (rmagic) + 1;
+ crypto_box_BOXZEROBYTES -
+ sizeof(rmagic) + 1;
if (boxlen < 0) {
return ret;
}
- box = malloc (boxlen);
- sodium_memzero (box, crypto_box_BOXZEROBYTES);
- memcpy (box + crypto_box_BOXZEROBYTES, p,
- boxlen - crypto_box_BOXZEROBYTES);
-
- if (crypto_box_open_afternm (box, box, boxlen, enonce, creq->nm->k) != -1) {
- memcpy (buf, box + crypto_box_ZEROBYTES,
- boxlen - crypto_box_ZEROBYTES);
+ box = malloc(boxlen);
+ sodium_memzero(box, crypto_box_BOXZEROBYTES);
+ memcpy(box + crypto_box_BOXZEROBYTES, p,
+ boxlen - crypto_box_BOXZEROBYTES);
+
+ if (crypto_box_open_afternm(box, box, boxlen, enonce, creq->nm->k) != -1) {
+ memcpy(buf, box + crypto_box_ZEROBYTES,
+ boxlen - crypto_box_ZEROBYTES);
ret = boxlen - crypto_box_ZEROBYTES;
*req_out = creq->req;
}
else {
- rdns_info ("unable open cryptobox of size %d", (int)boxlen);
+ rdns_info("unable open cryptobox of size %d", (int) boxlen);
}
- free (box);
+ free(box);
}
return ret;
}
-void
-rdns_curve_finish_request (struct rdns_request *req, void *plugin_data)
+void rdns_curve_finish_request(struct rdns_request *req, void *plugin_data)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
struct rdns_curve_request *creq = req->curve_plugin_data;
if (creq != NULL) {
- REF_RELEASE (creq->key);
- HASH_DELETE (hh, ctx->requests, creq);
+ REF_RELEASE(creq->key);
+ HASH_DELETE(hh, ctx->requests, creq);
}
}
-void
-rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data)
+void rdns_curve_dtor(struct rdns_resolver *resolver, void *plugin_data)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
if (ctx->key_refresh_event != NULL) {
- resolver->async->del_periodic (resolver->async->data,
- ctx->key_refresh_event);
+ resolver->async->del_periodic(resolver->async->data,
+ ctx->key_refresh_event);
}
- REF_RELEASE (ctx->cur_key);
+ REF_RELEASE(ctx->cur_key);
}
#elif defined(USE_RSPAMD_CRYPTOBOX)
@@ -454,13 +468,13 @@ rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data)
#define crypto_box_BOXZEROBYTES 16
#endif
-ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data,
- struct sockaddr *saddr, socklen_t slen);
-ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len,
- void *plugin_data, struct rdns_request **req_out,
- struct sockaddr *saddr, socklen_t slen);
-void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data);
-void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data);
+ssize_t rdns_curve_send(struct rdns_request *req, void *plugin_data,
+ struct sockaddr *saddr, socklen_t slen);
+ssize_t rdns_curve_recv(struct rdns_io_channel *ioc, void *buf, size_t len,
+ void *plugin_data, struct rdns_request **req_out,
+ struct sockaddr *saddr, socklen_t slen);
+void rdns_curve_finish_request(struct rdns_request *req, void *plugin_data);
+void rdns_curve_dtor(struct rdns_resolver *resolver, void *plugin_data);
struct rdns_curve_entry {
char *name;
@@ -502,35 +516,36 @@ struct rdns_curve_ctx {
};
static struct rdns_curve_client_key *
-rdns_curve_client_key_new (struct rdns_curve_ctx *ctx)
+rdns_curve_client_key_new(struct rdns_curve_ctx *ctx)
{
struct rdns_curve_client_key *new;
struct rdns_curve_nm_entry *nm;
struct rdns_curve_entry *entry, *tmp;
- new = calloc (1, sizeof (struct rdns_curve_client_key));
- rspamd_cryptobox_keypair (new->pk, new->sk, RSPAMD_CRYPTOBOX_MODE_25519);
+ new = calloc(1, sizeof(struct rdns_curve_client_key));
+ rspamd_cryptobox_keypair(new->pk, new->sk);
- HASH_ITER (hh, ctx->entries, entry, tmp) {
- nm = calloc (1, sizeof (struct rdns_curve_nm_entry));
+ HASH_ITER(hh, ctx->entries, entry, tmp)
+ {
+ nm = calloc(1, sizeof(struct rdns_curve_nm_entry));
nm->entry = entry;
- rspamd_cryptobox_nm (nm->k, entry->pk, new->sk,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_nm(nm->k, entry->pk, new->sk);
- DL_APPEND (new->nms, nm);
+ DL_APPEND(new->nms, nm);
}
- new->counter = ottery_rand_uint64 ();
+ new->counter = ottery_rand_uint64();
return new;
}
static struct rdns_curve_nm_entry *
-rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
+rdns_curve_find_nm(struct rdns_curve_client_key *key, struct rdns_curve_entry *entry)
{
struct rdns_curve_nm_entry *nm;
- DL_FOREACH (key->nms, nm) {
+ DL_FOREACH(key->nms, nm)
+ {
if (nm->entry == entry) {
return nm;
}
@@ -540,68 +555,69 @@ rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *
}
static void
-rdns_curve_client_key_free (struct rdns_curve_client_key *key)
+rdns_curve_client_key_free(struct rdns_curve_client_key *key)
{
struct rdns_curve_nm_entry *nm, *tmp;
- DL_FOREACH_SAFE (key->nms, nm, tmp) {
- rspamd_explicit_memzero (nm->k, sizeof (nm->k));
- free (nm);
+ DL_FOREACH_SAFE(key->nms, nm, tmp)
+ {
+ rspamd_explicit_memzero(nm->k, sizeof(nm->k));
+ free(nm);
}
- rspamd_explicit_memzero (key->sk, sizeof (key->sk));
- free (key);
+ rspamd_explicit_memzero(key->sk, sizeof(key->sk));
+ free(key);
}
-struct rdns_curve_ctx*
-rdns_curve_ctx_new (double key_refresh_interval)
+struct rdns_curve_ctx *
+rdns_curve_ctx_new(double key_refresh_interval)
{
struct rdns_curve_ctx *new;
- new = calloc (1, sizeof (struct rdns_curve_ctx));
+ new = calloc(1, sizeof(struct rdns_curve_ctx));
new->key_refresh_interval = key_refresh_interval;
return new;
}
-void
-rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
- const char *name, const unsigned char *pubkey)
+void rdns_curve_ctx_add_key(struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey)
{
struct rdns_curve_entry *entry;
bool success = true;
- entry = malloc (sizeof (struct rdns_curve_entry));
+ entry = malloc(sizeof(struct rdns_curve_entry));
if (entry != NULL) {
- entry->name = strdup (name);
+ entry->name = strdup(name);
if (entry->name == NULL) {
success = false;
}
- memcpy (entry->pk, pubkey, sizeof (entry->pk));
+ memcpy(entry->pk, pubkey, sizeof(entry->pk));
if (success) {
- HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry);
+ HASH_ADD_KEYPTR(hh, ctx->entries, entry->name, strlen(entry->name), entry);
}
}
}
-#define rdns_curve_write_hex(in, out, offset, base) do { \
- *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \
-} while (0)
+#define rdns_curve_write_hex(in, out, offset, base) \
+ do { \
+ *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \
+ } while (0)
static bool
-rdns_curve_hex_to_byte (const char *in, unsigned char *out)
+rdns_curve_hex_to_byte(const char *in, unsigned char *out)
{
int i;
- for (i = 0; i <= 1; i ++) {
+ for (i = 0; i <= 1; i++) {
if (in[i] >= '0' && in[i] <= '9') {
- rdns_curve_write_hex (in, out, i, '0');
+ rdns_curve_write_hex(in, out, i, '0');
}
else if (in[i] >= 'a' && in[i] <= 'f') {
- rdns_curve_write_hex (in, out, i, 'a' - 10);
+ rdns_curve_write_hex(in, out, i, 'a' - 10);
}
else if (in[i] >= 'A' && in[i] <= 'F') {
- rdns_curve_write_hex (in, out, i, 'A' - 10);
+ rdns_curve_write_hex(in, out, i, 'A' - 10);
}
else {
return false;
@@ -613,18 +629,18 @@ rdns_curve_hex_to_byte (const char *in, unsigned char *out)
#undef rdns_curve_write_hex
unsigned char *
-rdns_curve_key_from_hex (const char *hex)
+rdns_curve_key_from_hex(const char *hex)
{
- unsigned int len = strlen (hex), i;
+ unsigned int len = strlen(hex), i;
unsigned char *res = NULL;
- if (len == rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519) * 2) {
- res = calloc (1, rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519));
+ if (len == crypto_box_publickeybytes() * 2) {
+ res = calloc(1, crypto_box_publickeybytes());
for (i = 0;
- i < rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
- i ++) {
- if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) {
- free (res);
+ i < crypto_box_publickeybytes();
+ i++) {
+ if (!rdns_curve_hex_to_byte(&hex[i * 2], &res[i])) {
+ free(res);
return NULL;
}
}
@@ -633,35 +649,34 @@ rdns_curve_key_from_hex (const char *hex)
return res;
}
-void
-rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+void rdns_curve_ctx_destroy(struct rdns_curve_ctx *ctx)
{
struct rdns_curve_entry *entry, *tmp;
- HASH_ITER (hh, ctx->entries, entry, tmp) {
- free (entry->name);
- free (entry);
+ HASH_ITER(hh, ctx->entries, entry, tmp)
+ {
+ free(entry->name);
+ free(entry);
}
- free (ctx);
+ free(ctx);
}
static void
-rdns_curve_refresh_key_callback (void *user_data)
+rdns_curve_refresh_key_callback(void *user_data)
{
struct rdns_curve_ctx *ctx = user_data;
struct rdns_resolver *resolver;
resolver = ctx->resolver;
- rdns_info ("refresh dnscurve keys");
- REF_RELEASE (ctx->cur_key);
- ctx->cur_key = rdns_curve_client_key_new (ctx);
- REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+ rdns_info("refresh dnscurve keys");
+ REF_RELEASE(ctx->cur_key);
+ ctx->cur_key = rdns_curve_client_key_new(ctx);
+ REF_INIT_RETAIN(ctx->cur_key, rdns_curve_client_key_free);
}
-void
-rdns_curve_register_plugin (struct rdns_resolver *resolver,
- struct rdns_curve_ctx *ctx)
+void rdns_curve_register_plugin(struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx)
{
struct rdns_plugin *plugin;
@@ -669,7 +684,7 @@ rdns_curve_register_plugin (struct rdns_resolver *resolver,
return;
}
- plugin = calloc (1, sizeof (struct rdns_plugin));
+ plugin = calloc(1, sizeof(struct rdns_plugin));
if (plugin != NULL) {
plugin->data = ctx;
plugin->type = RDNS_PLUGIN_CURVE;
@@ -677,24 +692,24 @@ rdns_curve_register_plugin (struct rdns_resolver *resolver,
plugin->cb.curve_plugin.recv_cb = rdns_curve_recv;
plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request;
plugin->dtor = rdns_curve_dtor;
- ctx->cur_key = rdns_curve_client_key_new (ctx);
- REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free);
+ ctx->cur_key = rdns_curve_client_key_new(ctx);
+ REF_INIT_RETAIN(ctx->cur_key, rdns_curve_client_key_free);
if (ctx->key_refresh_interval > 0) {
- ctx->key_refresh_event = resolver->async->add_periodic (
- resolver->async->data, ctx->key_refresh_interval,
- rdns_curve_refresh_key_callback, ctx);
+ ctx->key_refresh_event = resolver->async->add_periodic(
+ resolver->async->data, ctx->key_refresh_interval,
+ rdns_curve_refresh_key_callback, ctx);
}
ctx->resolver = resolver;
- rdns_resolver_register_plugin (resolver, plugin);
+ rdns_resolver_register_plugin(resolver, plugin);
}
}
ssize_t
-rdns_curve_send (struct rdns_request *req, void *plugin_data,
- struct sockaddr *saddr, socklen_t slen)
+rdns_curve_send(struct rdns_request *req, void *plugin_data,
+ struct sockaddr *saddr, socklen_t slen)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
struct rdns_curve_entry *entry;
struct iovec iov[4];
unsigned char *m;
@@ -704,53 +719,52 @@ rdns_curve_send (struct rdns_request *req, void *plugin_data,
ssize_t ret, boxed_len;
/* Check for key */
- HASH_FIND_STR (ctx->entries, req->io->srv->name, entry);
+ HASH_FIND_STR(ctx->entries, req->io->srv->name, entry);
if (entry != NULL) {
- nm = rdns_curve_find_nm (ctx->cur_key, entry);
- creq = malloc (sizeof (struct rdns_curve_request));
+ nm = rdns_curve_find_nm(ctx->cur_key, entry);
+ creq = malloc(sizeof(struct rdns_curve_request));
if (creq == NULL) {
return -1;
}
boxed_len = req->pos + crypto_box_ZEROBYTES;
- m = malloc (boxed_len);
+ m = malloc(boxed_len);
if (m == NULL) {
free(creq);
return -1;
}
/* Ottery is faster than sodium native PRG that uses /dev/random only */
- memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t));
- ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t));
- rspamd_explicit_memzero (creq->nonce + 12,
- rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) - 12);
+ memcpy(creq->nonce, &ctx->cur_key->counter, sizeof(uint64_t));
+ ottery_rand_bytes(creq->nonce + sizeof(uint64_t), 12 - sizeof(uint64_t));
+ rspamd_explicit_memzero(creq->nonce + 12,
+ crypto_box_noncebytes() - 12);
- rspamd_explicit_memzero (m, crypto_box_ZEROBYTES);
- memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos);
+ rspamd_explicit_memzero(m, crypto_box_ZEROBYTES);
+ memcpy(m + crypto_box_ZEROBYTES, req->packet, req->pos);
- rspamd_cryptobox_encrypt_nm_inplace (m + crypto_box_ZEROBYTES,
- boxed_len,
- creq->nonce,
- nm->k,
- m,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_encrypt_nm_inplace(m + crypto_box_ZEROBYTES,
+ boxed_len,
+ creq->nonce,
+ nm->k,
+ m);
creq->key = ctx->cur_key;
- REF_RETAIN (ctx->cur_key);
+ REF_RETAIN(ctx->cur_key);
creq->entry = entry;
creq->req = req;
creq->nm = nm;
- HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq);
+ HASH_ADD_KEYPTR(hh, ctx->requests, creq->nonce, 12, creq);
req->curve_plugin_data = creq;
- ctx->cur_key->counter ++;
- ctx->cur_key->uses ++;
+ ctx->cur_key->counter++;
+ ctx->cur_key->uses++;
/* Now form a dnscurve packet */
- iov[0].iov_base = (void *)qmagic;
- iov[0].iov_len = sizeof (qmagic) - 1;
+ iov[0].iov_base = (void *) qmagic;
+ iov[0].iov_len = sizeof(qmagic) - 1;
iov[1].iov_base = ctx->cur_key->pk;
- iov[1].iov_len = sizeof (ctx->cur_key->pk);
+ iov[1].iov_len = sizeof(ctx->cur_key->pk);
iov[2].iov_base = creq->nonce;
iov[2].iov_len = 12;
iov[3].iov_base = m + crypto_box_BOXZEROBYTES;
@@ -758,17 +772,17 @@ rdns_curve_send (struct rdns_request *req, void *plugin_data,
struct msghdr msg;
- memset (&msg, 0, sizeof (msg));
+ memset(&msg, 0, sizeof(msg));
msg.msg_namelen = slen;
msg.msg_name = saddr;
msg.msg_iov = iov;
- msg.msg_iovlen = sizeof (iov) / sizeof (iov[0]);
- ret = sendmsg (req->io->sock, &msg, 0);
- rspamd_explicit_memzero (m, boxed_len);
- free (m);
+ msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
+ ret = sendmsg(req->io->sock, &msg, 0);
+ rspamd_explicit_memzero(m, boxed_len);
+ free(m);
}
else {
- ret = sendto (req->io->sock, req->packet, req->pos, 0, saddr, slen);
+ ret = sendto(req->io->sock, req->packet, req->pos, 0, saddr, slen);
req->curve_plugin_data = NULL;
}
@@ -776,10 +790,10 @@ rdns_curve_send (struct rdns_request *req, void *plugin_data,
}
ssize_t
-rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
- struct rdns_request **req_out, struct sockaddr *saddr, socklen_t slen)
+rdns_curve_recv(struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data,
+ struct rdns_request **req_out, struct sockaddr *saddr, socklen_t slen)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
ssize_t ret, boxlen;
static const char rmagic[] = "R6fnvWJ8";
unsigned char *p, *box;
@@ -788,102 +802,97 @@ rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugi
struct rdns_resolver *resolver;
resolver = ctx->resolver;
- ret = recv (ioc->sock, buf, len, 0);
+ ret = recv(ioc->sock, buf, len, 0);
if (ret <= 0 || ret < 64) {
/* Definitely not a DNSCurve packet */
return ret;
}
- if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) {
+ if (memcmp(buf, rmagic, sizeof(rmagic) - 1) == 0) {
/* Likely DNSCurve packet */
- p = ((unsigned char *)buf) + 8;
- HASH_FIND (hh, ctx->requests, p, 12, creq);
+ p = ((unsigned char *) buf) + 8;
+ HASH_FIND(hh, ctx->requests, p, 12, creq);
if (creq == NULL) {
- rdns_info ("unable to find nonce in the internal hash");
+ rdns_info("unable to find nonce in the internal hash");
return ret;
}
- memcpy (enonce, p, rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519));
- p += rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
- boxlen = ret - rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) +
- crypto_box_BOXZEROBYTES -
- sizeof (rmagic) + 1;
+ memcpy(enonce, p, crypto_box_noncebytes());
+ p += crypto_box_noncebytes();
+ boxlen = ret - crypto_box_noncebytes() +
+ crypto_box_BOXZEROBYTES -
+ sizeof(rmagic) + 1;
if (boxlen < 0) {
return ret;
}
- box = malloc (boxlen);
- rspamd_explicit_memzero (box, crypto_box_BOXZEROBYTES);
- memcpy (box + crypto_box_BOXZEROBYTES, p,
- boxlen - crypto_box_BOXZEROBYTES);
-
- if (!rspamd_cryptobox_decrypt_nm_inplace (
- box + rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
- boxlen - rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
- enonce, creq->nm->k, box, RSPAMD_CRYPTOBOX_MODE_25519)) {
- memcpy (buf, box + crypto_box_ZEROBYTES,
- boxlen - crypto_box_ZEROBYTES);
+ box = malloc(boxlen);
+ rspamd_explicit_memzero(box, crypto_box_BOXZEROBYTES);
+ memcpy(box + crypto_box_BOXZEROBYTES, p,
+ boxlen - crypto_box_BOXZEROBYTES);
+
+ if (!rspamd_cryptobox_decrypt_nm_inplace(
+ box + crypto_box_macbytes(),
+ boxlen - crypto_box_macbytes(),
+ enonce, creq->nm->k, box)) {
+ memcpy(buf, box + crypto_box_ZEROBYTES,
+ boxlen - crypto_box_ZEROBYTES);
ret = boxlen - crypto_box_ZEROBYTES;
*req_out = creq->req;
}
else {
- rdns_info ("unable open cryptobox of size %d", (int)boxlen);
+ rdns_info("unable open cryptobox of size %d", (int) boxlen);
}
- free (box);
+ free(box);
}
return ret;
}
-void
-rdns_curve_finish_request (struct rdns_request *req, void *plugin_data)
+void rdns_curve_finish_request(struct rdns_request *req, void *plugin_data)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
struct rdns_curve_request *creq = req->curve_plugin_data;
if (creq != NULL) {
- REF_RELEASE (creq->key);
- HASH_DELETE (hh, ctx->requests, creq);
+ REF_RELEASE(creq->key);
+ HASH_DELETE(hh, ctx->requests, creq);
}
}
-void
-rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data)
+void rdns_curve_dtor(struct rdns_resolver *resolver, void *plugin_data)
{
- struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data;
+ struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *) plugin_data;
if (ctx->key_refresh_event != NULL) {
- resolver->async->del_periodic (resolver->async->data,
- ctx->key_refresh_event);
+ resolver->async->del_periodic(resolver->async->data,
+ ctx->key_refresh_event);
}
- REF_RELEASE (ctx->cur_key);
+ REF_RELEASE(ctx->cur_key);
}
#else
/* Fake functions */
-struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval)
+struct rdns_curve_ctx *rdns_curve_ctx_new(double rekey_interval)
{
return NULL;
}
-void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx,
- const char *name, const unsigned char *pubkey)
+void rdns_curve_ctx_add_key(struct rdns_curve_ctx *ctx,
+ const char *name, const unsigned char *pubkey)
{
-
}
-void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx)
+void rdns_curve_ctx_destroy(struct rdns_curve_ctx *ctx)
{
-
}
-void rdns_curve_register_plugin (struct rdns_resolver *resolver,
- struct rdns_curve_ctx *ctx)
+void rdns_curve_register_plugin(struct rdns_resolver *resolver,
+ struct rdns_curve_ctx *ctx)
{
-
}
unsigned char *
-rdns_curve_key_from_hex (const char *hex)
+rdns_curve_key_from_hex(const char *hex)
{
return NULL;
}
diff --git a/contrib/libucl/lua_ucl.c b/contrib/libucl/lua_ucl.c
index a9edb3e4d..19ac9cb12 100644
--- a/contrib/libucl/lua_ucl.c
+++ b/contrib/libucl/lua_ucl.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2024 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
/* Copyright (c) 2014, Vsevolod Stakhov
* All rights reserved.
*
@@ -28,6 +44,7 @@
#include "ucl.h"
#include "ucl_internal.h"
#include "lua_ucl.h"
+#include "kvec.h"
#include <strings.h>
/***
@@ -69,6 +86,7 @@ func = "huh";
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
#define NULL_META "ucl.null.meta"
+#define ITER_META "ucl.object.iter"
#define OBJECT_META "ucl.object.meta"
#define UCL_OBJECT_TYPE_META "ucl.type.object"
#define UCL_ARRAY_TYPE_META "ucl.type.array"
@@ -79,10 +97,20 @@ static int ucl_object_lua_push_scalar(lua_State *L, const ucl_object_t *obj, int
static int ucl_object_push_lua_common(lua_State *L, const ucl_object_t *obj, int flags);
static ucl_object_t *ucl_object_lua_fromtable(lua_State *L, int idx, ucl_string_flags_t flags);
static ucl_object_t *ucl_object_lua_fromelt(lua_State *L, int idx, ucl_string_flags_t flags);
+static ucl_object_t *lua_ucl_object_get(lua_State *L, int index);
+static int lua_ucl_type(lua_State *L);
+static int lua_ucl_pairs(lua_State *L);
+static int lua_ucl_ipairs(lua_State *L);
+static int lua_ucl_len(lua_State *L);
+static int lua_ucl_index(lua_State *L);
+static int lua_ucl_newindex(lua_State *L);
+static int lua_ucl_object_tostring(lua_State *L);
+static int lua_ucl_object_unwrap(lua_State *L);
+static int lua_ucl_object_validate(lua_State *L);
static void *ucl_null;
-struct _rspamd_lua_text {
+struct rspamd_compat_lua_text {
const char *start;
unsigned int len;
unsigned int flags;
@@ -519,7 +547,7 @@ ucl_object_lua_fromelt(lua_State *L, int idx, ucl_string_flags_t flags)
}
else {
/* Assume it is a text like object */
- struct _rspamd_lua_text *t = lua_touserdata(L, idx);
+ struct rspamd_compat_lua_text *t = lua_touserdata(L, idx);
if (t) {
if (t->len > 0) {
@@ -551,7 +579,22 @@ ucl_object_lua_fromelt(lua_State *L, int idx, ucl_string_flags_t flags)
}
else {
if (type == LUA_TTABLE) {
- obj = ucl_object_lua_fromtable(L, idx, flags);
+ lua_rawgeti(L, idx, 0);
+
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ /* It is a cloaked ucl object */
+ obj = lua_ucl_object_get(L, idx);
+
+ if (obj) {
+ obj = ucl_object_ref(obj);
+ }
+ }
+
+ lua_pop(L, 1);
+
+ if (obj == NULL) {
+ obj = ucl_object_lua_fromtable(L, idx, flags);
+ }
}
else if (type == LUA_TFUNCTION) {
fd = malloc(sizeof(*fd));
@@ -679,16 +722,54 @@ lua_ucl_parser_get(lua_State *L, int index)
static ucl_object_t *
lua_ucl_object_get(lua_State *L, int index)
{
- return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
+ if (lua_istable(L, index)) {
+ ucl_object_t *res = NULL;
+ lua_rawgeti(L, index, 0);
+
+ if (lua_isuserdata(L, -1)) {
+ res = *((ucl_object_t **) lua_touserdata(L, -1));
+ }
+
+ lua_pop(L, 1);
+
+ return res;
+ }
+
+ return NULL;
}
-static void
-lua_ucl_push_opaque(lua_State *L, ucl_object_t *obj)
+void ucl_object_push_lua_unwrapped(lua_State *L, const ucl_object_t *obj)
{
ucl_object_t **pobj;
+ /* We create a plain lua table with the following elements:
+ * [0] - ucl object as userdata
+ * ["method"] - methods
+ */
+ lua_createtable(L, 1, 9);
pobj = lua_newuserdata(L, sizeof(*pobj));
- *pobj = obj;
+ *pobj = ucl_object_ref(obj);
+ lua_rawseti(L, -2, 0);
+
+ lua_pushcfunction(L, lua_ucl_index);
+ lua_setfield(L, -2, "at");
+ lua_pushcfunction(L, lua_ucl_type);
+ lua_setfield(L, -2, "type");
+ lua_pushcfunction(L, lua_ucl_pairs);
+ lua_setfield(L, -2, "pairs");
+ lua_pushcfunction(L, lua_ucl_ipairs);
+ lua_setfield(L, -2, "ipairs");
+ lua_pushcfunction(L, lua_ucl_len);
+ lua_setfield(L, -2, "len");
+ lua_pushcfunction(L, lua_ucl_object_tostring);
+ lua_setfield(L, -2, "tostring");
+ lua_pushcfunction(L, lua_ucl_object_unwrap);
+ lua_setfield(L, -2, "unwrap");
+ lua_pushcfunction(L, lua_ucl_object_unwrap);
+ lua_setfield(L, -2, "tolua");
+ lua_pushcfunction(L, lua_ucl_object_validate);
+ lua_setfield(L, -2, "validate");
+
luaL_getmetatable(L, OBJECT_META);
lua_setmetatable(L, -2);
}
@@ -878,7 +959,7 @@ static int
lua_ucl_parser_parse_text(lua_State *L)
{
struct ucl_parser *parser;
- struct _rspamd_lua_text *t;
+ struct rspamd_compat_lua_text *t;
enum ucl_parse_type type = UCL_PARSE_UCL;
int ret = 2;
@@ -890,7 +971,7 @@ lua_ucl_parser_parse_text(lua_State *L)
else if (lua_type(L, 2) == LUA_TSTRING) {
const char *s;
gsize len;
- static struct _rspamd_lua_text st_t;
+ static struct rspamd_compat_lua_text st_t;
s = lua_tolstring(L, 2, &len);
st_t.start = s;
@@ -969,7 +1050,8 @@ lua_ucl_parser_get_object_wrapped(lua_State *L)
obj = ucl_parser_get_object(parser);
if (obj != NULL) {
- lua_ucl_push_opaque(L, obj);
+ ucl_object_push_lua_unwrapped(L, obj);
+ ucl_object_unref(obj);
}
else {
lua_pushnil(L);
@@ -1128,8 +1210,9 @@ lua_ucl_object_tostring(lua_State *L)
enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
obj = lua_ucl_object_get(L, 1);
+ int type = ucl_object_type(obj);
- if (obj) {
+ if (type == UCL_ARRAY || type == UCL_OBJECT) {
if (lua_gettop(L) > 1) {
if (lua_type(L, 2) == LUA_TSTRING) {
const char *strtype = lua_tostring(L, 2);
@@ -1140,9 +1223,12 @@ lua_ucl_object_tostring(lua_State *L)
return lua_ucl_to_string(L, obj, format);
}
- else {
+ else if (type == UCL_NULL) {
lua_pushnil(L);
}
+ else {
+ ucl_object_lua_push_scalar(L, obj, 0);
+ }
return 1;
}
@@ -1211,7 +1297,8 @@ lua_ucl_object_validate(lua_State *L)
lua_pushnil(L);
if (ext_refs) {
- lua_ucl_push_opaque(L, ext_refs);
+ ucl_object_push_lua_unwrapped(L, ext_refs);
+ ucl_object_unref(ext_refs);
}
}
else {
@@ -1219,7 +1306,8 @@ lua_ucl_object_validate(lua_State *L)
lua_pushfstring(L, "validation error: %s", err.msg);
if (ext_refs) {
- lua_ucl_push_opaque(L, ext_refs);
+ ucl_object_push_lua_unwrapped(L, ext_refs);
+ ucl_object_unref(ext_refs);
}
}
}
@@ -1229,7 +1317,8 @@ lua_ucl_object_validate(lua_State *L)
lua_pushfstring(L, "cannot find the requested path: %s", path);
if (ext_refs) {
- lua_ucl_push_opaque(L, ext_refs);
+ ucl_object_push_lua_unwrapped(L, ext_refs);
+ ucl_object_unref(ext_refs);
}
}
}
@@ -1257,6 +1346,338 @@ lua_ucl_object_gc(lua_State *L)
return 0;
}
+static int
+lua_ucl_iter_gc(lua_State *L)
+{
+ ucl_object_iter_t it;
+
+ it = *((ucl_object_iter_t *) lua_touserdata(L, 1));
+
+ if (it) {
+ ucl_object_iterate_free(it);
+ }
+
+ return 0;
+}
+
+static int
+lua_ucl_index(lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get(L, 1);
+
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ /* Index by string */
+
+ if (ucl_object_type(obj) == UCL_OBJECT) {
+ size_t len;
+ const char *key = lua_tolstring(L, 2, &len);
+ const ucl_object_t *elt;
+
+ elt = ucl_object_lookup_len(obj, key, strlen(key));
+
+ if (elt) {
+ ucl_object_push_lua_unwrapped(L, elt);
+ }
+ else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+ }
+ else {
+ return luaL_error(L, "cannot index non-object: %s", ucl_object_type_to_string(ucl_object_type(obj)));
+ }
+ }
+ else if (lua_type(L, 2) == LUA_TNUMBER) {
+ /* Index by number */
+ if (ucl_object_type(obj) == UCL_ARRAY) {
+ /* +1 as Lua indexes elements from 1 and ucl indexes them from 0 */
+ lua_Integer idx = lua_tointeger(L, 2) + 1;
+ const ucl_object_t *elt;
+
+ elt = ucl_array_find_index(obj, idx);
+
+ if (elt) {
+ ucl_object_push_lua_unwrapped(L, elt);
+ }
+ else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+ }
+ else {
+ return luaL_error(L, "cannot index non-array: %s", ucl_object_type_to_string(ucl_object_type(obj)));
+ }
+ }
+ else {
+ return luaL_error(L, "invalid index type: %s", lua_typename(L, lua_type(L, 2)));
+ }
+}
+
+static int
+lua_ucl_newindex(lua_State *L)
+{
+ ucl_object_t *obj;
+ obj = lua_ucl_object_get(L, 1);
+ int key_type = lua_type(L, 2);
+
+ if (ucl_object_type(obj) == UCL_OBJECT) {
+ if (key_type == LUA_TSTRING) {
+ lua_Integer keylen;
+ const char *key = lua_tolstring(L, 2, &keylen);
+
+ ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+ if (value_obj) {
+ value_obj = ucl_object_ref(value_obj);
+ }
+ else {
+ value_obj = ucl_object_lua_import(L, 3);
+ }
+
+ if (ucl_object_lookup_len(obj, key, keylen) != NULL) {
+ if (value_obj != NULL) {
+ ucl_object_replace_key(obj, value_obj, key, keylen, true);
+ }
+ else {
+ /* Delete key */
+ ucl_object_delete_keyl(obj, key, keylen);
+ }
+ }
+ else {
+ if (value_obj != NULL) {
+ ucl_object_insert_key(obj, value_obj, key, keylen, true);
+ }
+ /* Do nothing if value_obj is null, like Lua does */
+ }
+ }
+ else if (key_type == LUA_TNUMBER) {
+ /*
+ * We have some object but wanted it to be an array
+ * In Lua, this is allowed but UCL has distinction between array and object
+ * Here, we allow one thing: if an object is empty, and we want a numeric index, we convert it to an array
+ */
+ lua_Integer idx = lua_tointeger(L, 2);
+ if (idx == 1 && obj->len == 0 && obj->value.ov == NULL) {
+ /* UCL preallocate arrays, but it is not a requirement once av is NULL */
+ obj->type = UCL_ARRAY;
+ ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+ if (value_obj) {
+ value_obj = ucl_object_ref(value_obj);
+ }
+ else {
+ value_obj = ucl_object_lua_import(L, 3);
+ }
+
+ if (value_obj == NULL) {
+ return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+ }
+ ucl_array_append(obj, value_obj);
+ }
+ else {
+ return luaL_error(L, "invalid index type for an object: %s", lua_typename(L, key_type));
+ }
+ }
+ else {
+ return luaL_error(L, "invalid index type for an object: %s", lua_typename(L, key_type));
+ }
+
+ return 0;
+ }
+ else if (ucl_object_type(obj) == UCL_ARRAY) {
+ if (key_type == LUA_TNUMBER) {
+ lua_Integer idx = lua_tointeger(L, 2);
+
+ ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+ if (value_obj) {
+ value_obj = ucl_object_ref(value_obj);
+ }
+ else {
+ value_obj = ucl_object_lua_import(L, 3);
+ }
+
+ if (value_obj == NULL) {
+ return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+ }
+
+ /* Lua allows sparse arrays and ucl does not
+ * So we have 2 options:
+ * 1) Idx is some existing index, so we need to replace it
+ * 3) Idx is #len, so we append it
+ * Everything else is an error
+ */
+ if (idx == ucl_array_size(obj) + 1) {
+ ucl_array_append(obj, value_obj);
+ }
+ else if (idx >= 1 && idx <= ucl_array_size(obj)) {
+ ucl_array_replace_index(obj, value_obj, idx - 1);
+ }
+ else {
+ ucl_object_unref(value_obj);
+
+ return luaL_error(L, "invalid index for array: %d", (int) idx);
+ }
+ }
+ else if (key_type == LUA_TSTRING) {
+ /*
+ * We have some array but wanted it to be an object
+ * In Lua, this is allowed but UCL has distinction between array and object
+ * Here, we allow one thing: if an array is empty, and we want a string index, we convert it to an object
+ * The biggest issue is that ucl array is preallocated in general, so we need to free it somehow
+ */
+ /*
+ * Dirty hacks are here
+ */
+ kvec_t(ucl_object_t *) *real_ar = obj->value.av;
+
+ if (real_ar) {
+ kv_destroy(*real_ar);
+ }
+ UCL_FREE(sizeof(*real_ar), real_ar);
+ obj->value.av = NULL;
+ obj->type = UCL_OBJECT;
+
+ lua_Integer keylen;
+ const char *key = lua_tolstring(L, 2, &keylen);
+
+ ucl_object_t *value_obj = lua_ucl_object_get(L, 3);
+ if (value_obj) {
+ value_obj = ucl_object_ref(value_obj);
+ }
+ else {
+ value_obj = ucl_object_lua_import(L, 3);
+ }
+
+ if (value_obj) {
+ ucl_object_insert_key(obj, value_obj, key, keylen, true);
+ }
+ else {
+ return luaL_error(L, "invalid value type: %s", lua_typename(L, lua_type(L, 3)));
+ }
+ }
+ else {
+ return luaL_error(L, "invalid index type for an array: %s", lua_typename(L, key_type));
+ }
+
+ return 0;
+ }
+ else {
+ return luaL_error(L, "invalid index type: %s (obj type: %s)", lua_typename(L, key_type),
+ ucl_object_type_to_string(ucl_object_type(obj)));
+ }
+}
+
+static int
+lua_ucl_type(lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get(L, 1);
+ lua_pushstring(L, ucl_object_type_to_string(ucl_object_type(obj)));
+
+ return 1;
+}
+
+static int
+lua_ucl_object_iter(lua_State *L)
+{
+ ucl_object_iter_t it;
+ const ucl_object_t *cur;
+
+ it = *((ucl_object_iter_t *) lua_touserdata(L, 1));
+ cur = ucl_object_iterate_safe(it, true);
+
+ if (cur) {
+ if (ucl_object_key(cur)) {
+ size_t klen;
+ const char *k = ucl_object_keyl(cur, &klen);
+ lua_pushlstring(L, k, klen);
+ }
+ else {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_Integer idx = lua_tointeger(L, 2);
+ if (idx >= 0) {
+ lua_pushinteger(L, idx + 1);
+ }
+ }
+ else {
+ lua_pushnumber(L, -1);
+ }
+ }
+ ucl_object_push_lua_unwrapped(L, cur);
+
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+
+ return 1;
+ }
+}
+
+static int
+lua_ucl_pairs(lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get(L, 1);
+ int t = ucl_object_type(obj);
+
+ if ((obj) && (t == UCL_ARRAY || t == UCL_OBJECT || obj->next != NULL)) {
+ /* iter_func, ucl_object_t, iter */
+ lua_pushcfunction(L, lua_ucl_object_iter);
+ ucl_object_iter_t *pit = lua_newuserdata(L, sizeof(ucl_object_iter_t *));
+ luaL_getmetatable(L, ITER_META);
+ lua_setmetatable(L, -2);
+ ucl_object_iter_t it = ucl_object_iterate_new(obj);
+ *pit = it;
+ lua_pushnumber(L, -1);
+
+ return 3;
+ }
+ else {
+ return luaL_error(L, "invalid object type for pairs: %s", ucl_object_type_to_string(t));
+ }
+}
+
+static int
+lua_ucl_len(lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get(L, 1);
+ lua_pushinteger(L, obj->len);
+
+ return 1;
+}
+
+static int
+lua_ucl_ipairs(lua_State *L)
+{
+ ucl_object_t *obj;
+
+ obj = lua_ucl_object_get(L, 1);
+ int t = ucl_object_type(obj);
+
+ if ((obj) && (t == UCL_ARRAY || obj->next != NULL)) {
+ /* iter_func, ucl_object_t, iter */
+ lua_pushcfunction(L, lua_ucl_object_iter);
+ ucl_object_iter_t *pit = lua_newuserdata(L, sizeof(ucl_object_iter_t *));
+ luaL_getmetatable(L, ITER_META);
+ lua_setmetatable(L, -2);
+ ucl_object_iter_t it = ucl_object_iterate_new(obj);
+ *pit = it;
+ lua_pushnumber(L, 0);
+
+ return 3;
+ }
+ else {
+ return luaL_error(L, "invalid object type for ipairs: %s", ucl_object_type_to_string(t));
+ }
+}
+
static void
lua_ucl_parser_mt(lua_State *L)
{
@@ -1300,29 +1721,41 @@ lua_ucl_object_mt(lua_State *L)
{
luaL_newmetatable(L, OBJECT_META);
- lua_pushvalue(L, -1);
+ lua_pushcfunction(L, lua_ucl_index);
lua_setfield(L, -2, "__index");
+ lua_pushcfunction(L, lua_ucl_newindex);
+ lua_setfield(L, -2, "__newindex");
+ /* Usable merely with lua 5.2+ */
+ lua_pushcfunction(L, lua_ucl_ipairs);
+ lua_setfield(L, -2, "__ipairs");
+ /* Usable merely with lua 5.2+ */
+ lua_pushcfunction(L, lua_ucl_pairs);
+ lua_setfield(L, -2, "__pairs");
+
+ /* Access UCL elements using `:at` method */
+ lua_pushcfunction(L, lua_ucl_index);
+ lua_setfield(L, -2, "at");
+
+ /* Usable merely with lua 5.2+ */
+ lua_pushcfunction(L, lua_ucl_len);
+ lua_setfield(L, -2, "__len");
lua_pushcfunction(L, lua_ucl_object_gc);
lua_setfield(L, -2, "__gc");
-
lua_pushcfunction(L, lua_ucl_object_tostring);
lua_setfield(L, -2, "__tostring");
- lua_pushcfunction(L, lua_ucl_object_tostring);
- lua_setfield(L, -2, "tostring");
-
- lua_pushcfunction(L, lua_ucl_object_unwrap);
- lua_setfield(L, -2, "unwrap");
+ lua_pushstring(L, OBJECT_META);
+ lua_setfield(L, -2, "class");
- lua_pushcfunction(L, lua_ucl_object_unwrap);
- lua_setfield(L, -2, "tolua");
+ lua_pop(L, 1);
- lua_pushcfunction(L, lua_ucl_object_validate);
- lua_setfield(L, -2, "validate");
+ luaL_newmetatable(L, ITER_META);
+ lua_pushcfunction(L, lua_ucl_iter_gc);
+ lua_setfield(L, -2, "__gc");
- lua_pushstring(L, OBJECT_META);
- lua_setfield(L, -2, "class");
+ lua_pushstring(L, ITER_META);
+ lua_setfield(L, -2, "__tostring");
lua_pop(L, 1);
}
diff --git a/contrib/libucl/lua_ucl.h b/contrib/libucl/lua_ucl.h
index 4a759e3b4..fe3d86dfd 100644
--- a/contrib/libucl/lua_ucl.h
+++ b/contrib/libucl/lua_ucl.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Vsevolod Stakhov
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,6 +91,13 @@ UCL_EXTERN ucl_object_t *ucl_object_lua_import_escape(lua_State *L, int idx);
*/
UCL_EXTERN int ucl_object_push_lua(lua_State *L,
const ucl_object_t *obj, bool allow_array);
+
+/**
+ * Push an object to lua as userdata object (handling one refcount)
+ * @param L
+ * @param obj
+ */
+UCL_EXTERN void ucl_object_push_lua_unwrapped(lua_State *L, const ucl_object_t *obj);
/**
* Push an object to lua replacing all ucl.null with `false`
* @param L lua state
diff --git a/contrib/publicsuffix/README.md b/contrib/publicsuffix/README.md
new file mode 100644
index 000000000..eb11436ff
--- /dev/null
+++ b/contrib/publicsuffix/README.md
@@ -0,0 +1,13 @@
+# Public suffixes list
+
+Update procedure:
+
+1. Download the list from the [official mirror](https://publicsuffix.org/list/public_suffix_list.dat)
+2. Proceed through `idn.pl` script
+
+1 liner: `curl https://publicsuffix.org/list/public_suffix_list.dat | perl idn.pl > effective_tld_names.dat`
+
+## Deps installation
+
+Ensure that you have `cpanm` installed (e.g. by `brew install cpanm`).
+Run `cpanm --installdeps .` once.
diff --git a/contrib/publicsuffix/cpanfile b/contrib/publicsuffix/cpanfile
new file mode 100644
index 000000000..401e27443
--- /dev/null
+++ b/contrib/publicsuffix/cpanfile
@@ -0,0 +1,3 @@
+requires 'Net::IDN::Encode', '2.500';
+requires 'Unicode::Normalize', '1.0';
+
diff --git a/contrib/publicsuffix/effective_tld_names.dat b/contrib/publicsuffix/effective_tld_names.dat
index b0a3524cd..bbb7ec699 100644
--- a/contrib/publicsuffix/effective_tld_names.dat
+++ b/contrib/publicsuffix/effective_tld_names.dat
@@ -32,22 +32,30 @@ ac.ae
gov.ae
mil.ae
-// aero : see https://www.information.aero/index.php?id=66
+// aero : https://information.aero/registration/policies/dmp
aero
+// 2LDs
+airline.aero
+airport.aero
+// 2LDs (currently not accepting registration, seemingly never have)
+// As of 2024-07, these are marked as reserved for potential 3LD
+// registrations (clause 11 "allocated subdomains" in the 2006 TLD
+// policy), but the relevant industry partners have not opened them up
+// for registration. Current status can be determined from the TLD's
+// policy document: 2LDs that are open for registration must list
+// their policy in the TLD's policy. Any 2LD without such a policy is
+// not open for registrations.
accident-investigation.aero
accident-prevention.aero
aerobatic.aero
aeroclub.aero
aerodrome.aero
agents.aero
-aircraft.aero
-airline.aero
-airport.aero
air-surveillance.aero
-airtraffic.aero
air-traffic-control.aero
+aircraft.aero
+airtraffic.aero
ambulance.aero
-amusement.aero
association.aero
author.aero
ballooning.aero
@@ -78,6 +86,7 @@ exchange.aero
express.aero
federation.aero
flight.aero
+freight.aero
fuel.aero
gliding.aero
government.aero
@@ -92,6 +101,7 @@ leasing.aero
logistics.aero
magazine.aero
maintenance.aero
+marketplace.aero
media.aero
microlight.aero
modelling.aero
@@ -114,6 +124,7 @@ show.aero
skydiving.aero
software.aero
student.aero
+taxi.aero
trader.aero
trading.aero
trainer.aero
@@ -675,7 +686,6 @@ mil.by
// second-level domain, but it's being used as one (see www.google.com.by and
// www.yahoo.com.by, for example), so we list it here for safety's sake.
com.by
-
// http://hoster.by/
of.by
@@ -1019,13 +1029,12 @@ net.et
// eu : https://en.wikipedia.org/wiki/.eu
eu
-// fi : https://en.wikipedia.org/wiki/.fi
+// fi : https://www.iana.org/domains/root/db/fi.html
fi
-// aland.fi : https://en.wikipedia.org/wiki/.ax
+// aland.fi : https://www.iana.org/domains/root/db/ax.html
// This domain is being phased out in favor of .ax. As there are still many
// domains under aland.fi, we still keep it on the list until aland.fi is
// completely removed.
-// TODO: Check for updates (expected to be phased out around Q1/2009)
aland.fi
// fj : http://domains.fj/
@@ -2094,18 +2103,18 @@ xn--d5qv7z876c.jp
// jp geographic type names
// http://jprs.jp/doc/rule/saisoku-1.html
*.kawasaki.jp
-*.kitakyushu.jp
-*.kobe.jp
-*.nagoya.jp
-*.sapporo.jp
-*.sendai.jp
-*.yokohama.jp
!city.kawasaki.jp
+*.kitakyushu.jp
!city.kitakyushu.jp
+*.kobe.jp
!city.kobe.jp
+*.nagoya.jp
!city.nagoya.jp
+*.sapporo.jp
!city.sapporo.jp
+*.sendai.jp
!city.sendai.jp
+*.yokohama.jp
!city.yokohama.jp
// 4th level registration
aisai.aichi.jp
@@ -5325,22 +5334,27 @@ ngo.ph
mil.ph
i.ph
-// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK
+// pk : https://pknic.net.pk
+// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK + grandfathered old gon.pk
+// Contact Email: staff@pknic.net.pk PKNIC .PK Registry
+
pk
+ac.pk
+biz.pk
com.pk
-net.pk
edu.pk
-org.pk
fam.pk
-biz.pk
-web.pk
-gov.pk
+gkp.pk
gob.pk
+gog.pk
gok.pk
gon.pk
gop.pk
gos.pk
-info.pk
+gov.pk
+net.pk
+org.pk
+web.pk
// pl http://www.dns.pl/english/index.html
// Submitted by registry
@@ -7044,10 +7058,9 @@ gov.zw
mil.zw
org.zw
-
// newGTLDs
-// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-02-08T15:13:14Z
+// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-08-10T15:15:39Z
// This list is auto-generated, don't edit it manually.
// aaa : American Automobile Association, Inc.
// https://www.iana.org/domains/root/db/aaa.html
@@ -7233,7 +7246,7 @@ anquan
// https://www.iana.org/domains/root/db/anz.html
anz
-// aol : Oath Inc.
+// aol : Yahoo Inc.
// https://www.iana.org/domains/root/db/aol.html
aol
@@ -7325,10 +7338,6 @@ auto
// https://www.iana.org/domains/root/db/autos.html
autos
-// avianca : Avianca Inc.
-// https://www.iana.org/domains/root/db/avianca.html
-avianca
-
// aws : AWS Registry LLC
// https://www.iana.org/domains/root/db/aws.html
aws
@@ -8693,10 +8702,6 @@ grocery
// https://www.iana.org/domains/root/db/group.html
group
-// guardian : The Guardian Life Insurance Company of America
-// https://www.iana.org/domains/root/db/guardian.html
-guardian
-
// gucci : Guccio Gucci S.p.a.
// https://www.iana.org/domains/root/db/gucci.html
gucci
@@ -9321,7 +9326,7 @@ lotte
// https://www.iana.org/domains/root/db/lotto.html
lotto
-// love : Merchant Law Group LLP
+// love : Waterford Limited
// https://www.iana.org/domains/root/db/love.html
love
@@ -9369,7 +9374,7 @@ maison
// https://www.iana.org/domains/root/db/makeup.html
makeup
-// man : MAN SE
+// man : MAN Truck & Bus SE
// https://www.iana.org/domains/root/db/man.html
man
@@ -9573,10 +9578,6 @@ nab
// https://www.iana.org/domains/root/db/nagoya.html
nagoya
-// natura : NATURA COSMÉTICOS S.A.
-// https://www.iana.org/domains/root/db/natura.html
-natura
-
// navy : Dog Beach, LLC
// https://www.iana.org/domains/root/db/navy.html
navy
@@ -10349,10 +10350,6 @@ shangrila
// https://www.iana.org/domains/root/db/sharp.html
sharp
-// shaw : Shaw Cablesystems G.P.
-// https://www.iana.org/domains/root/db/shaw.html
-shaw
-
// shell : Shell Information Technology International Inc
// https://www.iana.org/domains/root/db/shell.html
shell
@@ -10845,7 +10842,7 @@ ups
// https://www.iana.org/domains/root/db/vacations.html
vacations
-// vana : Internet Naming Company LLC
+// vana : D3 Registry LLC
// https://www.iana.org/domains/root/db/vana.html
vana
@@ -11555,7 +11552,7 @@ xyz
// https://www.iana.org/domains/root/db/yachts.html
yachts
-// yahoo : Oath Inc.
+// yahoo : Yahoo Inc.
// https://www.iana.org/domains/root/db/yahoo.html
yahoo
@@ -11617,9 +11614,27 @@ zuerich
// ===END ICANN DOMAINS===
+
// ===BEGIN PRIVATE DOMAINS===
+
// (Note: these are in alphabetical order by company name)
+// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf
+co.krd
+edu.krd
+
+// .pl domains (grandfathered)
+art.pl
+gliwice.pl
+krakow.pl
+poznan.pl
+wroc.pl
+zakopane.pl
+
+// .US
+// Submitted by Ed Moore <Ed.Moore@lib.de.us>
+lib.de.us
+
// 12CHARS: https://12chars.com
// Submitted by Kenny Niehage <psl@12chars.com>
12chars.dev
@@ -11632,7 +11647,7 @@ cc.ua
inf.ua
ltd.ua
-// 611coin : https://611project.org/
+// 611 blockchain domain name system : https://611project.net/
611.to
// A2 Hosting
@@ -11640,13 +11655,9 @@ ltd.ua
a2hosted.com
cpserver.com
-// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za
-// Submitted by Aaron Marais <its_me@aaronleem.co.za>
-graphox.us
-
-// accesso Technology Group, plc. : https://accesso.com/
-// Submitted by accesso Team <accessoecommerce@accesso.com>
-*.devcdnaccesso.com
+// AAA workspace : https://aaa.vodka
+// Submitted by Kirill Rezraf <admin@aaa.vodka>
+aaa.vodka
// Acorn Labs : https://acorn.io
// Submitted by Craig Jellick <domains@acorn.io>
@@ -11656,6 +11667,10 @@ graphox.us
// Submitted by Ofer Kalaora <postmaster@activetrail.com>
activetrail.biz
+// Adaptable.io : https://adaptable.io
+// Submitted by Mark Terrel <support@adaptable.io>
+adaptable.app
+
// Adobe : https://www.adobe.com/
// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com>
adobeaemcloud.com
@@ -11672,6 +11687,10 @@ hlx3.page
adobeio-static.net
adobeioruntime.net
+// Africa.com Web Solutions Ltd : https://registry.africa.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+africa.com
+
// Agnat sp. z o.o. : https://domena.pl
// Submitted by Przemyslaw Plewa <it-admin@domena.pl>
beep.pl
@@ -11777,23 +11796,30 @@ cloudfront.net
// Amazon Cognito
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: 7bee1013-f456-47df-bfe8-03c78d946d61
+// Reference: cb38c251-c93d-4cda-81ec-e72c4f0fdb72
auth.af-south-1.amazoncognito.com
+auth.ap-east-1.amazoncognito.com
auth.ap-northeast-1.amazoncognito.com
auth.ap-northeast-2.amazoncognito.com
auth.ap-northeast-3.amazoncognito.com
auth.ap-south-1.amazoncognito.com
+auth.ap-south-2.amazoncognito.com
auth.ap-southeast-1.amazoncognito.com
auth.ap-southeast-2.amazoncognito.com
auth.ap-southeast-3.amazoncognito.com
+auth.ap-southeast-4.amazoncognito.com
auth.ca-central-1.amazoncognito.com
+auth.ca-west-1.amazoncognito.com
auth.eu-central-1.amazoncognito.com
+auth.eu-central-2.amazoncognito.com
auth.eu-north-1.amazoncognito.com
auth.eu-south-1.amazoncognito.com
+auth.eu-south-2.amazoncognito.com
auth.eu-west-1.amazoncognito.com
auth.eu-west-2.amazoncognito.com
auth.eu-west-3.amazoncognito.com
auth.il-central-1.amazoncognito.com
+auth.me-central-1.amazoncognito.com
auth.me-south-1.amazoncognito.com
auth.sa-east-1.amazoncognito.com
auth.us-east-1.amazoncognito.com
@@ -11809,14 +11835,14 @@ auth-fips.us-west-2.amazoncognito.com
// Amazon EC2
// Submitted by Luke Wells <psl-maintainers@amazon.com>
// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537
+*.compute.amazonaws.com.cn
*.compute.amazonaws.com
*.compute-1.amazonaws.com
-*.compute.amazonaws.com.cn
us-east-1.amazonaws.com
// Amazon EMR
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: 597f3f8e-9283-4e48-8e32-7ee25a1ff6ab
+// Reference: 82f43f9f-bbb8-400e-8349-854f5a62f20d
emrappui-prod.cn-north-1.amazonaws.com.cn
emrnotebooks-prod.cn-north-1.amazonaws.com.cn
emrstudio-prod.cn-north-1.amazonaws.com.cn
@@ -11841,6 +11867,9 @@ emrstudio-prod.ap-northeast-3.amazonaws.com
emrappui-prod.ap-south-1.amazonaws.com
emrnotebooks-prod.ap-south-1.amazonaws.com
emrstudio-prod.ap-south-1.amazonaws.com
+emrappui-prod.ap-south-2.amazonaws.com
+emrnotebooks-prod.ap-south-2.amazonaws.com
+emrstudio-prod.ap-south-2.amazonaws.com
emrappui-prod.ap-southeast-1.amazonaws.com
emrnotebooks-prod.ap-southeast-1.amazonaws.com
emrstudio-prod.ap-southeast-1.amazonaws.com
@@ -11850,18 +11879,30 @@ emrstudio-prod.ap-southeast-2.amazonaws.com
emrappui-prod.ap-southeast-3.amazonaws.com
emrnotebooks-prod.ap-southeast-3.amazonaws.com
emrstudio-prod.ap-southeast-3.amazonaws.com
+emrappui-prod.ap-southeast-4.amazonaws.com
+emrnotebooks-prod.ap-southeast-4.amazonaws.com
+emrstudio-prod.ap-southeast-4.amazonaws.com
emrappui-prod.ca-central-1.amazonaws.com
emrnotebooks-prod.ca-central-1.amazonaws.com
emrstudio-prod.ca-central-1.amazonaws.com
+emrappui-prod.ca-west-1.amazonaws.com
+emrnotebooks-prod.ca-west-1.amazonaws.com
+emrstudio-prod.ca-west-1.amazonaws.com
emrappui-prod.eu-central-1.amazonaws.com
emrnotebooks-prod.eu-central-1.amazonaws.com
emrstudio-prod.eu-central-1.amazonaws.com
+emrappui-prod.eu-central-2.amazonaws.com
+emrnotebooks-prod.eu-central-2.amazonaws.com
+emrstudio-prod.eu-central-2.amazonaws.com
emrappui-prod.eu-north-1.amazonaws.com
emrnotebooks-prod.eu-north-1.amazonaws.com
emrstudio-prod.eu-north-1.amazonaws.com
emrappui-prod.eu-south-1.amazonaws.com
emrnotebooks-prod.eu-south-1.amazonaws.com
emrstudio-prod.eu-south-1.amazonaws.com
+emrappui-prod.eu-south-2.amazonaws.com
+emrnotebooks-prod.eu-south-2.amazonaws.com
+emrstudio-prod.eu-south-2.amazonaws.com
emrappui-prod.eu-west-1.amazonaws.com
emrnotebooks-prod.eu-west-1.amazonaws.com
emrstudio-prod.eu-west-1.amazonaws.com
@@ -11871,6 +11912,9 @@ emrstudio-prod.eu-west-2.amazonaws.com
emrappui-prod.eu-west-3.amazonaws.com
emrnotebooks-prod.eu-west-3.amazonaws.com
emrstudio-prod.eu-west-3.amazonaws.com
+emrappui-prod.il-central-1.amazonaws.com
+emrnotebooks-prod.il-central-1.amazonaws.com
+emrstudio-prod.il-central-1.amazonaws.com
emrappui-prod.me-central-1.amazonaws.com
emrnotebooks-prod.me-central-1.amazonaws.com
emrstudio-prod.me-central-1.amazonaws.com
@@ -11901,23 +11945,37 @@ emrstudio-prod.us-west-2.amazonaws.com
// Amazon Managed Workflows for Apache Airflow
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: 4ab55e6f-90c0-4a8d-b6a0-52ca5dbb1c2e
+// Reference: f5ea5d0a-ec6a-4f23-ac1c-553fbff13f5c
*.cn-north-1.airflow.amazonaws.com.cn
*.cn-northwest-1.airflow.amazonaws.com.cn
+*.af-south-1.airflow.amazonaws.com
+*.ap-east-1.airflow.amazonaws.com
*.ap-northeast-1.airflow.amazonaws.com
*.ap-northeast-2.airflow.amazonaws.com
+*.ap-northeast-3.airflow.amazonaws.com
*.ap-south-1.airflow.amazonaws.com
+*.ap-south-2.airflow.amazonaws.com
*.ap-southeast-1.airflow.amazonaws.com
*.ap-southeast-2.airflow.amazonaws.com
+*.ap-southeast-3.airflow.amazonaws.com
+*.ap-southeast-4.airflow.amazonaws.com
*.ca-central-1.airflow.amazonaws.com
+*.ca-west-1.airflow.amazonaws.com
*.eu-central-1.airflow.amazonaws.com
+*.eu-central-2.airflow.amazonaws.com
*.eu-north-1.airflow.amazonaws.com
+*.eu-south-1.airflow.amazonaws.com
+*.eu-south-2.airflow.amazonaws.com
*.eu-west-1.airflow.amazonaws.com
*.eu-west-2.airflow.amazonaws.com
*.eu-west-3.airflow.amazonaws.com
+*.il-central-1.airflow.amazonaws.com
+*.me-central-1.airflow.amazonaws.com
+*.me-south-1.airflow.amazonaws.com
*.sa-east-1.airflow.amazonaws.com
*.us-east-1.airflow.amazonaws.com
*.us-east-2.airflow.amazonaws.com
+*.us-west-1.airflow.amazonaws.com
*.us-west-2.airflow.amazonaws.com
// Amazon S3
@@ -12211,9 +12269,25 @@ s3-fips.us-west-2.amazonaws.com
s3-object-lambda.us-west-2.amazonaws.com
s3-website.us-west-2.amazonaws.com
+// Amazon SageMaker Ground Truth
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: 98dbfde4-7802-48c3-8751-b60f204e0d9c
+labeling.ap-northeast-1.sagemaker.aws
+labeling.ap-northeast-2.sagemaker.aws
+labeling.ap-south-1.sagemaker.aws
+labeling.ap-southeast-1.sagemaker.aws
+labeling.ap-southeast-2.sagemaker.aws
+labeling.ca-central-1.sagemaker.aws
+labeling.eu-central-1.sagemaker.aws
+labeling.eu-west-1.sagemaker.aws
+labeling.eu-west-2.sagemaker.aws
+labeling.us-east-1.sagemaker.aws
+labeling.us-east-2.sagemaker.aws
+labeling.us-west-2.sagemaker.aws
+
// Amazon SageMaker Notebook Instances
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: ce8ae0b1-0070-496d-be88-37c31837af9d
+// Reference: b5ea56df-669e-43cc-9537-14aa172f5dfc
notebook.af-south-1.sagemaker.aws
notebook.ap-east-1.sagemaker.aws
notebook.ap-northeast-1.sagemaker.aws
@@ -12250,6 +12324,7 @@ notebook-fips.us-gov-east-1.sagemaker.aws
notebook.us-gov-west-1.sagemaker.aws
notebook-fips.us-gov-west-1.sagemaker.aws
notebook.us-west-1.sagemaker.aws
+notebook-fips.us-west-1.sagemaker.aws
notebook.us-west-2.sagemaker.aws
notebook-fips.us-west-2.sagemaker.aws
notebook.cn-north-1.sagemaker.com.cn
@@ -12257,7 +12332,7 @@ notebook.cn-northwest-1.sagemaker.com.cn
// Amazon SageMaker Studio
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: 057ee397-6bf8-4f20-b807-d7bc145ac980
+// Reference: 69c723d9-6e1a-4bff-a203-48eecd203183
studio.af-south-1.sagemaker.aws
studio.ap-east-1.sagemaker.aws
studio.ap-northeast-1.sagemaker.aws
@@ -12271,6 +12346,7 @@ studio.ca-central-1.sagemaker.aws
studio.eu-central-1.sagemaker.aws
studio.eu-north-1.sagemaker.aws
studio.eu-south-1.sagemaker.aws
+studio.eu-south-2.sagemaker.aws
studio.eu-west-1.sagemaker.aws
studio.eu-west-2.sagemaker.aws
studio.eu-west-3.sagemaker.aws
@@ -12289,6 +12365,11 @@ studio.us-west-2.sagemaker.aws
studio.cn-north-1.sagemaker.com.cn
studio.cn-northwest-1.sagemaker.com.cn
+// Amazon SageMaker with MLflow
+// Submited by: AWS Security <psl-maintainers@amazon.com>
+// Reference: c19f92b3-a82a-452d-8189-831b572eea7e
+*.experiments.sagemaker.aws
+
// Analytics on AWS
// Submitted by AWS Security <psl-maintainers@amazon.com>
// Reference: 955f9f40-a495-4e73-ae85-67b77ac9cadd
@@ -12305,8 +12386,8 @@ analytics-gateway.us-west-2.amazonaws.com
// AWS Amplify
// Submitted by AWS Security <psl-maintainers@amazon.com>
-// Reference: 5ecce854-c033-4fc4-a755-1a9916d9a9bb
-*.amplifyapp.com
+// Reference: c35bed18-6f4f-424f-9298-5756f2f7d72b
+amplifyapp.com
// AWS App Runner
// Submitted by AWS Security <psl-maintainers@amazon.com>
@@ -12382,6 +12463,11 @@ webview-assets.aws-cloud9.us-west-2.amazonaws.com
vfs.cloud9.us-west-2.amazonaws.com
webview-assets.cloud9.us-west-2.amazonaws.com
+// AWS Directory Service
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Reference: a13203e8-42dc-4045-a0d2-2ee67bed1068
+awsapps.com
+
// AWS Elastic Beanstalk
// Submitted by AWS Security <psl-maintainers@amazon.com>
// Reference: bb5a965c-dec3-4967-aa22-e306ad064797
@@ -12438,11 +12524,6 @@ eero-stage.online
// concludes Amazon
-// Amune : https://amune.org/
-// Submitted by Team Amune <cert@amune.org>
-t3l3p0rt.net
-tele.amune.org
-
// Apigee : https://apigee.com/
// Submitted by Apigee Security Team <security@apigee.com>
apigee.io
@@ -12468,6 +12549,10 @@ appudo.net
// Submitted by Thomas Orozco <thomas@aptible.com>
on-aptible.com
+// Aquapal : https://aquapal.net/
+// Submitted by Aki Ueno <admin@aquapal.net>
+f5.si
+
// ASEINet : https://www.aseinet.com/
// Submitted by Asei SEKIGUCHI <mail@aseinet.com>
user.aseinet.ne.jp
@@ -12503,6 +12588,7 @@ autocode.dev
// AVM : https://avm.de
// Submitted by Andreas Weise <a.weise@avm.de>
+myfritz.link
myfritz.net
// AVStack Pte. Ltd. : https://avstack.io
@@ -12522,19 +12608,10 @@ ecommerce-shop.pl
// Submitted by Olivier Benz <olivier.benz@b-data.ch>
b-data.io
-// backplane : https://www.backplane.io
-// Submitted by Anthony Voutas <anthony@backplane.io>
-backplaneapp.io
-
// Balena : https://www.balena.io
// Submitted by Petros Angelatos <petrosagg@balena.io>
balena-devices.com
-// University of Banja Luka : https://unibl.org
-// Domains for Republic of Srpska administrative entity.
-// Submitted by Marko Ivanovic <kormang@hotmail.rs>
-rs.ba
-
// Banzai Cloud
// Submitted by Janos Matyas <info@banzaicloud.com>
*.banzai.cloud
@@ -12574,6 +12651,10 @@ betainabox.com
// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au>
bnr.la
+// Bip : https://bip.sh
+// Submitted by Joel Kennedy <joel@bip.sh>
+bip.sh
+
// Bitbucket : http://bitbucket.org
// Submitted by Andy Ortlieb <aortlieb@atlassian.com>
bitbucket.io
@@ -12616,13 +12697,19 @@ square7.net
*.s.brave.io
// Brendly : https://brendly.rs
-// Submitted by Dusan Radovanovic <dusan.radovanovic@brendly.rs>
+// Submitted by Dusan Radovanovic <administracija@brendly.rs>
+shop.brendly.hr
shop.brendly.rs
// BrowserSafetyMark
// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com>
browsersafetymark.io
+// BRS Media : https://brsmedia.com/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+radio.am
+radio.fm
+
// Bytemark Hosting : https://www.bytemark.co.uk
// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
uk0.bigv.io
@@ -12640,7 +12727,9 @@ mycd.eu
// Canva Pty Ltd : https://canva.com/
// Submitted by Joel Aquilina <publicsuffixlist@canva.com>
canva-apps.cn
+*.my.canvasite.cn
canva-apps.com
+*.my.canva.site
// Carrd : https://carrd.co
// Submitted by AJ <aj@carrd.co>
@@ -12650,71 +12739,42 @@ carrd.co
crd.co
ju.mp
+// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk
+// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk>
+api.gov.uk
+
+// CDN77.com : http://www.cdn77.com
+// Submitted by Jan Krpes <jan.krpes@cdn77.com>
+cdn77-storage.com
+rsc.contentproxy9.cz
+r.cdn77.net
+cdn77-ssl.net
+c.cdn77.org
+rsc.cdn77.org
+ssl.origin.cdn77-secure.org
+
// CentralNic : http://www.centralnic.com/names/domains
// Submitted by registry <gavin.brown@centralnic.com>
-ae.org
+za.bz
br.com
cn.com
-com.de
-com.se
de.com
eu.com
-gb.net
-hu.net
-jp.net
jpn.com
mex.com
ru.com
sa.com
-se.net
uk.com
-uk.net
us.com
-za.bz
za.com
-
-// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-ar.com
-hu.com
-kr.com
-no.com
-qc.com
-uy.com
-
-// Africa.com Web Solutions Ltd : https://registry.africa.com
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-africa.com
-
-// iDOT Services Limited : http://www.domain.gr.com
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-gr.com
-
-// Radix FZC : http://domains.in.net
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-in.net
-web.in
-
-// US REGISTRY LLC : http://us.org
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-us.org
-
-// co.com Registry, LLC : https://registry.co.com
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-co.com
-
-// Roar Domains LLC : https://roar.basketball/
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-aus.basketball
-nz.basketball
-
-// BRS Media : https://brsmedia.com/
-// Submitted by Gavin Brown <gavin.brown@centralnic.com>
-radio.am
-radio.fm
-
-// c.la : http://www.c.la/
-c.la
+com.de
+gb.net
+hu.net
+jp.net
+se.net
+uk.net
+ae.org
+com.se
// certmgr.org : https://certmgr.org
// Submitted by B. Blechschmidt <hostmaster@certmgr.org>
@@ -12729,10 +12789,6 @@ cx.ua
discourse.group
discourse.team
-// Clever Cloud : https://www.clever-cloud.com/
-// Submitted by Quentin Adam <noc@clever-cloud.com>
-cleverapps.io
-
// Clerk : https://www.clerk.dev
// Submitted by Colin Sidoti <systems@clerk.dev>
clerk.app
@@ -12742,10 +12798,40 @@ clerkstage.app
*.stg.dev
*.stgstage.dev
+// Clever Cloud : https://www.clever-cloud.com/
+// Submitted by Quentin Adam <noc@clever-cloud.com>
+cleverapps.cc
+*.services.clever-cloud.com
+cleverapps.io
+cleverapps.tech
+
// ClickRising : https://clickrising.com/
// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com>
clickrising.net
+// Cloud DNS Ltd : http://www.cloudns.net
+// Submitted by Aleksander Hristov <noc@cloudns.net> & Boyan Peychev <boyan@cloudns.net>
+cloudns.asia
+cloudns.be
+cloudns.biz
+cloudns.cc
+cloudns.ch
+cloudns.cl
+cloudns.club
+dnsabr.com
+cloudns.cx
+cloudns.eu
+cloudns.in
+cloudns.info
+dns-cloud.net
+dns-dynamic.net
+cloudns.nz
+cloudns.org
+cloudns.ph
+cloudns.pro
+cloudns.pw
+cloudns.us
+
// Cloud66 : https://www.cloud66.com/
// Submitted by Khash Sajadi <khash@cloud66.com>
c66.me
@@ -12760,11 +12846,6 @@ cloudaccess.host
freesite.host
cloudaccess.net
-// cloudControl : https://www.cloudcontrol.com/
-// Submitted by Tobias Wilken <tw@cloudcontrol.com>
-cloudcontrolled.com
-cloudcontrolapp.com
-
// Cloudera, Inc. : https://www.cloudera.com/
// Submitted by Kedarnath Waikar <security@cloudera.com>
*.cloudera.site
@@ -12777,52 +12858,46 @@ trycloudflare.com
pages.dev
r2.dev
workers.dev
+cloudflare.net
+cdn.cloudflare.net
+cdn.cloudflareanycast.net
+cdn.cloudflarecn.net
+cdn.cloudflareglobal.net
+
+// cloudscale.ch AG : https://www.cloudscale.ch/
+// Submitted by Gaudenz Steinlin <support@cloudscale.ch>
+cust.cloudscale.ch
+objects.lpg.cloudscale.ch
+objects.rma.cloudscale.ch
// Clovyr : https://clovyr.io
// Submitted by Patrick Nielsen <patrick@clovyr.io>
wnext.app
-// co.ca : http://registry.co.ca/
-co.ca
+// CNPY : https://cnpy.gdn
+// Submitted by Angelo Gladding <angelo@lahacker.net>
+cnpy.gdn
// Co & Co : https://co-co.nl/
// Submitted by Govert Versluis <govert@co-co.nl>
*.otap.co
-// i-registry s.r.o. : http://www.i-registry.cz/
-// Submitted by Martin Semrad <semrad@i-registry.cz>
-co.cz
-
-// CDN77.com : http://www.cdn77.com
-// Submitted by Jan Krpes <jan.krpes@cdn77.com>
-c.cdn77.org
-cdn77-ssl.net
-r.cdn77.net
-rsc.cdn77.org
-ssl.origin.cdn77-secure.org
-
-// Cloud DNS Ltd : http://www.cloudns.net
-// Submitted by Aleksander Hristov <noc@cloudns.net>
-cloudns.asia
-cloudns.biz
-cloudns.club
-cloudns.cc
-cloudns.eu
-cloudns.in
-cloudns.info
-cloudns.org
-cloudns.pro
-cloudns.pw
-cloudns.us
+// co.ca : http://registry.co.ca/
+co.ca
-// CNPY : https://cnpy.gdn
-// Submitted by Angelo Gladding <angelo@lahacker.net>
-cnpy.gdn
+// co.com Registry, LLC : https://registry.co.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+co.com
// Codeberg e. V. : https://codeberg.org
// Submitted by Moritz Marquardt <git@momar.de>
codeberg.page
+// CodeSandbox B.V. : https://codesandbox.io
+// Submitted by Ives van Hoorne <abuse@codesandbox.io>
+csb.app
+preview.csb.app
+
// CoDNS B.V.
co.nl
co.no
@@ -12832,6 +12907,10 @@ co.no
webhosting.be
hosting-cluster.nl
+// Convex : https://convex.dev/
+// Submitted by James Cowling <security@convex.dev>
+convex.site
+
// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/
// Submitted by George Georgievsky <gug@cctld.ru>
ac.ru
@@ -12844,8 +12923,8 @@ test.ru
// COSIMO GmbH : http://www.cosimo.de
// Submitted by Rene Marticke <rmarticke@cosimo.de>
dyn.cosidns.de
-dynamisches-dns.de
dnsupdater.de
+dynamisches-dns.de
internet-dns.de
l-o-g-i-n.de
dynamic-dns.info
@@ -12853,22 +12932,22 @@ feste-ip.net
knx-server.net
static-access.net
-// cPanel L.L.C. : https://www.cpanel.net/
-// Submitted by Dustin Scherer <public.suffix@cpanel.net>
-*.cprapid.com
+// Craft Docs Ltd : https://www.craft.do/
+// Submitted by Zsombor Fuszenecker <security@craft.do>
+craft.me
// Craynic, s.r.o. : http://www.craynic.com/
// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
realm.cz
+// Crisp IM SAS : https://crisp.chat/
+// Submitted by Baptiste Jamin <hostmaster@crisp.chat>
+on.crisp.email
+
// Cryptonomic : https://cryptonomic.net/
// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net>
*.cryptonomic.net
-// Cupcake : https://cupcake.io/
-// Submitted by Jonathan Rudenberg <jonathan@cupcake.io>
-cupcake.is
-
// Curv UG : https://curv-labs.de/
// Submitted by Marvin Wiesner <Marvin@curv-labs.de>
curv.dev
@@ -12881,12 +12960,9 @@ curv.dev
*.ocp.customer-oci.com
*.ocs.customer-oci.com
-// Cyclic Software : https://www.cyclic.sh
-// Submitted by Kam Lasater <dns-admin@cyclic.sh>
-cyclic.app
-cyclic.cloud
-cyclic-app.com
-cyclic.co.in
+// cyber_Folks S.A. : https://cyberfolks.pl
+// Submitted by Bartlomiej Kida <security@cyberfolks.pl>
+cfolks.pl
// cyon GmbH : https://www.cyon.ch/
// Submitted by Dominic Luechinger <dol@cyon.ch>
@@ -12895,23 +12971,9 @@ cyon.site
// Danger Science Group: https://dangerscience.com/
// Submitted by Skylar MacDonald <skylar@dangerscience.com>
+platform0.app
fnwk.site
folionetwork.site
-platform0.app
-
-// Daplie, Inc : https://daplie.com
-// Submitted by AJ ONeal <aj@daplie.com>
-daplie.me
-localhost.daplie.me
-
-// Datto, Inc. : https://www.datto.com/
-// Submitted by Philipp Heckel <ph@datto.com>
-dattolocal.com
-dattorelay.com
-dattoweb.com
-mydatto.com
-dattolocal.net
-mydatto.net
// Dansk.net : http://www.dansk.net/
// Submitted by Anani Voule <digital@digital.co.dk>
@@ -12921,6 +12983,11 @@ firm.dk
reg.dk
store.dk
+// Daplie, Inc : https://daplie.com
+// Submitted by AJ ONeal <aj@daplie.com>
+daplie.me
+localhost.daplie.me
+
// dappnode.io : https://dappnode.io/
// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io>
dyndns.dappnode.io
@@ -12933,6 +13000,7 @@ dyndns.dappnode.io
// Dark, Inc. : https://darklang.com
// Submitted by Paul Biggar <ops@darklang.com>
builtwithdark.com
+darklang.io
// DataDetect, LLC. : https://datadetect.com
// Submitted by Andrew Banchich <abanchich@sceven.com>
@@ -12943,14 +13011,40 @@ instance.datadetect.com
// Submitted by Richard Li <secalert@datawire.io>
edgestack.me
+// Datto, Inc. : https://www.datto.com/
+// Submitted by Philipp Heckel <ph@datto.com>
+dattolocal.com
+dattorelay.com
+dattoweb.com
+mydatto.com
+dattolocal.net
+mydatto.net
+
// DDNS5 : https://ddns5.com
// Submitted by Cameron Elliott <cameron@cameronelliott.com>
ddns5.com
+// ddnss.de : https://www.ddnss.de/
+// Submitted by Robert Niedziela <webmaster@ddnss.de>
+ddnss.de
+dyn.ddnss.de
+dyndns.ddnss.de
+dyn-ip24.de
+dyndns1.de
+home-webserver.de
+dyn.home-webserver.de
+myhome-server.de
+ddnss.org
+
// Debian : https://www.debian.org/
// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org>
debian.net
+// Definima : http://www.definima.com/
+// Submitted by Maxence Bitterli <maxence@definima.com>
+definima.io
+definima.net
+
// Deno Land Inc : https://deno.com/
// Submitted by Luca Casonato <hostmaster@deno.com>
deno.dev
@@ -12965,6 +13059,24 @@ dedyn.io
deta.app
deta.dev
+// dhosting.pl Sp. z o.o.: https://dhosting.pl/
+// Submitted by Michal Kokoszkiewicz <bok@dhosting.pl>
+dfirma.pl
+dkonto.pl
+you2.pl
+
+// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/
+// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com>
+ondigitalocean.app
+
+// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/
+// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com>
+*.digitaloceanspaces.com
+
+// DigitalPlat : https://www.digitalplat.org/
+// Submitted by Edward Hsing <contact@digitalplat.org>
+us.kg
+
// Diher Solutions : https://diher.solutions
// Submitted by Didi Hermawan <mail@diher.solutions>
*.rss.my.id
@@ -12983,6 +13095,10 @@ jozi.biz
// Submitted by Norbert Auler <mail@dnshome.de>
dnshome.de
+// dnstrace.pro : https://dnstrace.pro/
+// Submitted by Chris Partridge <chris@partridge.tech>
+bci.dnstrace.pro
+
// DotArai : https://www.dotarai.com/
// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th>
online.th
@@ -13000,6 +13116,10 @@ shoparena.pl
// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com>
dreamhosters.com
+// Dreamyoungs, Inc. : https://durumis.com
+// Submitted by Infra Team <infra@durumis.com>
+durumis.com
+
// Drobo : http://www.drobo.com/
// Submitted by Ricardo Padilha <rpadilha@drobo.com>
mydrobo.com
@@ -13013,19 +13133,32 @@ drud.us
// Submitted by Richard Harper <richard@duckdns.org>
duckdns.org
-// Bip : https://bip.sh
-// Submitted by Joel Kennedy <joel@bip.sh>
-bip.sh
-
-// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com
-bitbridge.net
-
// dy.fi : http://dy.fi/
// Submitted by Heikki Hannikainen <hessu@hes.iki.fi>
dy.fi
tunk.org
// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
+dyndns.biz
+for-better.biz
+for-more.biz
+for-some.biz
+for-the.biz
+selfip.biz
+webhop.biz
+ftpaccess.cc
+game-server.cc
+myphotos.cc
+scrapping.cc
+blogdns.com
+cechire.com
+dnsalias.com
+dnsdojo.com
+doesntexist.com
+dontexist.com
+doomdns.com
+dyn-o-saur.com
+dynalias.com
dyndns-at-home.com
dyndns-at-work.com
dyndns-blog.com
@@ -13040,64 +13173,14 @@ dyndns-server.com
dyndns-web.com
dyndns-wiki.com
dyndns-work.com
-dyndns.biz
-dyndns.info
-dyndns.org
-dyndns.tv
-at-band-camp.net
-ath.cx
-barrel-of-knowledge.info
-barrell-of-knowledge.info
-better-than.tv
-blogdns.com
-blogdns.net
-blogdns.org
-blogsite.org
-boldlygoingnowhere.org
-broke-it.net
-buyshouses.net
-cechire.com
-dnsalias.com
-dnsalias.net
-dnsalias.org
-dnsdojo.com
-dnsdojo.net
-dnsdojo.org
-does-it.net
-doesntexist.com
-doesntexist.org
-dontexist.com
-dontexist.net
-dontexist.org
-doomdns.com
-doomdns.org
-dvrdns.org
-dyn-o-saur.com
-dynalias.com
-dynalias.net
-dynalias.org
-dynathome.net
-dyndns.ws
-endofinternet.net
-endofinternet.org
-endoftheinternet.org
est-a-la-maison.com
est-a-la-masion.com
est-le-patron.com
est-mon-blogueur.com
-for-better.biz
-for-more.biz
-for-our.info
-for-some.biz
-for-the.biz
-forgot.her.name
-forgot.his.name
from-ak.com
from-al.com
from-ar.com
-from-az.net
from-ca.com
-from-co.net
from-ct.com
from-dc.com
from-de.com
@@ -13110,10 +13193,8 @@ from-il.com
from-in.com
from-ks.com
from-ky.com
-from-la.net
from-ma.com
from-md.com
-from-me.org
from-mi.com
from-mn.com
from-mo.com
@@ -13126,7 +13207,6 @@ from-nh.com
from-nj.com
from-nm.com
from-nv.com
-from-ny.net
from-oh.com
from-ok.com
from-or.com
@@ -13144,45 +13224,18 @@ from-wa.com
from-wi.com
from-wv.com
from-wy.com
-ftpaccess.cc
-fuettertdasnetz.de
-game-host.org
-game-server.cc
getmyip.com
-gets-it.net
-go.dyndns.org
gotdns.com
-gotdns.org
-groks-the.info
-groks-this.info
-ham-radio-op.net
-here-for-more.info
hobby-site.com
-hobby-site.org
-home.dyndns.org
-homedns.org
-homeftp.net
-homeftp.org
-homeip.net
homelinux.com
-homelinux.net
-homelinux.org
homeunix.com
-homeunix.net
-homeunix.org
iamallama.com
-in-the-band.net
is-a-anarchist.com
is-a-blogger.com
is-a-bookkeeper.com
-is-a-bruinsfan.org
is-a-bulls-fan.com
-is-a-candidate.org
is-a-caterer.com
-is-a-celticsfan.org
is-a-chef.com
-is-a-chef.net
-is-a-chef.org
is-a-conservative.com
is-a-cpa.com
is-a-cubicle-slave.com
@@ -13191,31 +13244,25 @@ is-a-designer.com
is-a-doctor.com
is-a-financialadvisor.com
is-a-geek.com
-is-a-geek.net
-is-a-geek.org
is-a-green.com
is-a-guru.com
is-a-hard-worker.com
is-a-hunter.com
-is-a-knight.org
is-a-landscaper.com
is-a-lawyer.com
is-a-liberal.com
is-a-libertarian.com
-is-a-linux-user.org
is-a-llama.com
is-a-musician.com
is-a-nascarfan.com
is-a-nurse.com
is-a-painter.com
-is-a-patsfan.org
is-a-personaltrainer.com
is-a-photographer.com
is-a-player.com
is-a-republican.com
is-a-rockstar.com
is-a-socialist.com
-is-a-soxfan.org
is-a-student.com
is-a-teacher.com
is-a-techie.com
@@ -13227,113 +13274,150 @@ is-an-anarchist.com
is-an-artist.com
is-an-engineer.com
is-an-entertainer.com
-is-by.us
is-certified.com
-is-found.org
is-gone.com
is-into-anime.com
is-into-cars.com
is-into-cartoons.com
is-into-games.com
is-leet.com
-is-lost.org
is-not-certified.com
-is-saved.org
is-slick.com
is-uberleet.com
-is-very-bad.org
-is-very-evil.org
-is-very-good.org
-is-very-nice.org
-is-very-sweet.org
is-with-theband.com
isa-geek.com
-isa-geek.net
-isa-geek.org
isa-hockeynut.com
issmarterthanyou.com
+likes-pie.com
+likescandy.com
+neat-url.com
+saves-the-whales.com
+selfip.com
+sells-for-less.com
+sells-for-u.com
+servebbs.com
+simple-url.com
+space-to-rent.com
+teaches-yoga.com
+writesthisblog.com
+ath.cx
+fuettertdasnetz.de
isteingeek.de
istmein.de
-kicks-ass.net
-kicks-ass.org
-knowsitall.info
-land-4-sale.us
lebtimnetz.de
leitungsen.de
-likes-pie.com
-likescandy.com
+traeumtgerade.de
+barrel-of-knowledge.info
+barrell-of-knowledge.info
+dyndns.info
+for-our.info
+groks-the.info
+groks-this.info
+here-for-more.info
+knowsitall.info
+selfip.info
+webhop.info
+forgot.her.name
+forgot.his.name
+at-band-camp.net
+blogdns.net
+broke-it.net
+buyshouses.net
+dnsalias.net
+dnsdojo.net
+does-it.net
+dontexist.net
+dynalias.net
+dynathome.net
+endofinternet.net
+from-az.net
+from-co.net
+from-la.net
+from-ny.net
+gets-it.net
+ham-radio-op.net
+homeftp.net
+homeip.net
+homelinux.net
+homeunix.net
+in-the-band.net
+is-a-chef.net
+is-a-geek.net
+isa-geek.net
+kicks-ass.net
+office-on-the.net
+podzone.net
+scrapper-site.net
+selfip.net
+sells-it.net
+servebbs.net
+serveftp.net
+thruhere.net
+webhop.net
merseine.nu
mine.nu
+shacknet.nu
+blogdns.org
+blogsite.org
+boldlygoingnowhere.org
+dnsalias.org
+dnsdojo.org
+doesntexist.org
+dontexist.org
+doomdns.org
+dvrdns.org
+dynalias.org
+dyndns.org
+go.dyndns.org
+home.dyndns.org
+endofinternet.org
+endoftheinternet.org
+from-me.org
+game-host.org
+gotdns.org
+hobby-site.org
+homedns.org
+homeftp.org
+homelinux.org
+homeunix.org
+is-a-bruinsfan.org
+is-a-candidate.org
+is-a-celticsfan.org
+is-a-chef.org
+is-a-geek.org
+is-a-knight.org
+is-a-linux-user.org
+is-a-patsfan.org
+is-a-soxfan.org
+is-found.org
+is-lost.org
+is-saved.org
+is-very-bad.org
+is-very-evil.org
+is-very-good.org
+is-very-nice.org
+is-very-sweet.org
+isa-geek.org
+kicks-ass.org
misconfused.org
-mypets.ws
-myphotos.cc
-neat-url.com
-office-on-the.net
-on-the-web.tv
-podzone.net
podzone.org
readmyblog.org
-saves-the-whales.com
-scrapper-site.net
-scrapping.cc
-selfip.biz
-selfip.com
-selfip.info
-selfip.net
selfip.org
-sells-for-less.com
-sells-for-u.com
-sells-it.net
sellsyourhome.org
-servebbs.com
-servebbs.net
servebbs.org
-serveftp.net
serveftp.org
servegame.org
-shacknet.nu
-simple-url.com
-space-to-rent.com
stuff-4-sale.org
-stuff-4-sale.us
-teaches-yoga.com
-thruhere.net
-traeumtgerade.de
-webhop.biz
-webhop.info
-webhop.net
webhop.org
+better-than.tv
+dyndns.tv
+on-the-web.tv
worse-than.tv
-writesthisblog.com
-
-// ddnss.de : https://www.ddnss.de/
-// Submitted by Robert Niedziela <webmaster@ddnss.de>
-ddnss.de
-dyn.ddnss.de
-dyndns.ddnss.de
-dyndns1.de
-dyn-ip24.de
-home-webserver.de
-dyn.home-webserver.de
-myhome-server.de
-ddnss.org
-
-// Definima : http://www.definima.com/
-// Submitted by Maxence Bitterli <maxence@definima.com>
-definima.net
-definima.io
-
-// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/
-// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com>
-ondigitalocean.app
-
-// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/
-// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com>
-*.digitaloceanspaces.com
-
-// dnstrace.pro : https://dnstrace.pro/
-// Submitted by Chris Partridge <chris@partridge.tech>
-bci.dnstrace.pro
+is-by.us
+land-4-sale.us
+stuff-4-sale.us
+dyndns.ws
+mypets.ws
// Dynu.com : https://www.dynu.com/
// Submitted by Sue Ye <sue@dynu.com>
@@ -13368,6 +13452,31 @@ e4.cz
easypanel.app
easypanel.host
+// EasyWP : https://www.easywp.com
+// Submitted by <infracloudteam@namecheap.com>
+*.ewp.live
+
+// ECG Robotics, Inc: https://ecgrobotics.org
+// Submitted by <frc1533@ecgrobotics.org>
+onred.one
+staging.onred.one
+
+// eDirect Corp. : https://hosting.url.com.tw/
+// Submitted by C.S. chang <cschang@corp.url.com.tw>
+twmail.cc
+twmail.net
+twmail.org
+mymailer.com.tw
+url.tw
+
+// Electromagnetic Field : https://www.emfcamp.org
+// Submitted by <noc@emfcamp.org>
+at.emf.camp
+
+// Elefunc, Inc. : https://elefunc.com
+// Submitted by Cetin Sert <domains@elefunc.com>
+rt.ht
+
// Elementor : Elementor Ltd.
// Submitted by Anton Barkan <antonb@elementor.com>
elementor.cloud
@@ -13378,7 +13487,7 @@ elementor.cool
en-root.fr
// Enalean SAS: https://www.enalean.com
-// Submitted by Thomas Cottier <thomas.cottier@enalean.com>
+// Submitted by Enalean Security Team <security@enalean.com>
mytuleap.com
tuleap-partners.com
@@ -13387,11 +13496,6 @@ tuleap-partners.com
encr.app
encoreapi.com
-// ECG Robotics, Inc: https://ecgrobotics.org
-// Submitted by <frc1533@ecgrobotics.org>
-onred.one
-staging.onred.one
-
// encoway GmbH : https://www.encoway.de
// Submitted by Marcel Daus <cloudops@encoway.de>
eu.encoway.cloud
@@ -13470,13 +13574,15 @@ us-2.evennode.com
us-3.evennode.com
us-4.evennode.com
-// eDirect Corp. : https://hosting.url.com.tw/
-// Submitted by C.S. chang <cschang@corp.url.com.tw>
-twmail.cc
-twmail.net
-twmail.org
-mymailer.com.tw
-url.tw
+// Evervault : https://evervault.com
+// Submitted by Hannah Neary <engineering@evervault.com>
+relay.evervault.app
+relay.evervault.dev
+
+// Expo : https://expo.dev/
+// Submitted by James Ide <psl@expo.dev>
+expo.app
+staging.expo.app
// Fabrica Technologies, Inc. : https://www.fabrica.dev/
// Submitted by Eric Jiang <eric@fabrica.dev>
@@ -13568,8 +13674,6 @@ u.channelsdvr.net
edgecompute.app
fastly-edge.com
fastly-terrarium.com
-fastlylb.net
-map.fastlylb.net
freetls.fastly.net
map.fastly.net
a.prod.fastly.net
@@ -13577,6 +13681,8 @@ global.prod.fastly.net
a.ssl.fastly.net
b.ssl.fastly.net
global.ssl.fastly.net
+fastlylb.net
+map.fastlylb.net
// Fastmail : https://www.fastmail.com/
// Submitted by Marc Bradshaw <marc@fastmailteam.com>
@@ -13590,6 +13696,12 @@ myfast.host
fastvps.site
myfast.space
+// FearWorks Media Ltd. : https://fearworksmedia.co.uk
+// submitted by Keith Fairley <domains@fearworksmedia.co.uk>
+conn.uk
+copro.uk
+hosp.uk
+
// Fedora : https://fedoraproject.org/
// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org>
fedorainfracloud.org
@@ -13598,12 +13710,6 @@ cloud.fedoraproject.org
app.os.fedoraproject.org
app.os.stg.fedoraproject.org
-// FearWorks Media Ltd. : https://fearworksmedia.co.uk
-// submitted by Keith Fairley <domains@fearworksmedia.co.uk>
-conn.uk
-copro.uk
-hosp.uk
-
// Fermax : https://fermax.com/
// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be>
mydobiss.com
@@ -13615,12 +13721,6 @@ fh-muenster.io
// Filegear Inc. : https://www.filegear.com
// Submitted by Jason Zhu <jason@owtware.com>
filegear.me
-filegear-au.me
-filegear-de.me
-filegear-gb.me
-filegear-ie.me
-filegear-jp.me
-filegear-sg.me
// Firebase, Inc.
// Submitted by Chris Raynor <chris@firebase.com>
@@ -13636,7 +13736,6 @@ flap.id
// FlashDrive : https://flashdrive.io
// Submitted by Eric Chan <support@flashdrive.io>
-onflashdrive.app
fldrv.com
// FlutterFlow : https://flutterflow.io
@@ -13646,12 +13745,8 @@ flutterflow.app
// fly.io: https://fly.io
// Submitted by Kurt Mackey <kurt@fly.io>
fly.dev
-edgeapp.net
shw.io
-
-// Flynn : https://flynn.io
-// Submitted by Jonathan Rudenberg <jonathan@flynn.io>
-flynnhosting.net
+edgeapp.net
// Forgerock : https://www.forgerock.com
// Submitted by Roderick Parr <roderick.parr@forgerock.com>
@@ -13659,7 +13754,8 @@ forgeblocks.com
id.forgerock.io
// Framer : https://www.framer.com
-// Submitted by Koen Rouwhorst <koenrh@framer.com>
+// Submitted by Koen Rouwhorst <security@framer.com>
+framer.ai
framer.app
framercanvas.com
framer.media
@@ -13667,14 +13763,6 @@ framer.photos
framer.website
framer.wiki
-// Frusky MEDIA&PR : https://www.frusky.de
-// Submitted by Victor Pupynin <hallo@frusky.de>
-*.frusky.de
-
-// RavPage : https://www.ravpage.co.il
-// Submitted by Roni Horowitz <roni@responder.co.il>
-ravpage.co.il
-
// Frederik Braun https://frederik-braun.com
// Submitted by Frederik Braun <fb@frederik-braun.com>
0e.vc
@@ -13696,10 +13784,32 @@ freedesktop.org
// Submitted by Cadence <contact@freemyip.com>
freemyip.com
+// Frusky MEDIA&PR : https://www.frusky.de
+// Submitted by Victor Pupynin <hallo@frusky.de>
+*.frusky.de
+
// FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at
// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at>
wien.funkfeuer.at
+// Future Versatile Group. : https://www.fvg-on.net/
+// T.Kabu <webmaster@fvg-on.net>
+daemon.asia
+dix.asia
+mydns.bz
+0am.jp
+0g0.jp
+0j0.jp
+0t0.jp
+mydns.jp
+pgw.jp
+wjg.jp
+keyword-on.net
+live-on.net
+server-on.net
+mydns.tw
+mydns.vc
+
// Futureweb GmbH : https://www.futureweb.at
// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at>
*.futurecms.at
@@ -13717,6 +13827,8 @@ aliases121.com
// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names
// Submitted by Stephen Ford <hostmaster@digital.cabinet-office.gov.uk>
+campaign.gov.uk
+service.gov.uk
independent-commission.uk
independent-inquest.uk
independent-inquiry.uk
@@ -13724,12 +13836,6 @@ independent-panel.uk
independent-review.uk
public-inquiry.uk
royal-commission.uk
-campaign.gov.uk
-service.gov.uk
-
-// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk
-// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk>
-api.gov.uk
// Gehirn Inc. : https://www.gehirn.co.jp/
// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp>
@@ -13743,9 +13849,11 @@ gentlentapis.com
lab.ms
cdn-edges.net
-// Ghost Foundation : https://ghost.org
-// Submitted by Matt Hanley <security@ghost.org>
-ghost.io
+// Getlocalcert: https://www.getlocalcert.net
+// Submitted by Robert Alexander <support@getlocalcert.net>
+localcert.net
+localhostcert.net
+corpnet.work
// GignoSystemJapan: http://gsj.bz
// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz>
@@ -13889,89 +13997,73 @@ whitesnow.jp
zombie.jp
heteml.net
-// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
-// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk>
-cloudapps.digital
-london.cloudapps.digital
-
-// GOV.UK Pay : https://www.payments.service.gov.uk/
-// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk>
-pymnt.uk
-
-// GlobeHosting, Inc.
-// Submitted by Zoltan Egresi <egresi@globehosting.com>
-ro.im
+// GoDaddy Registry : https://registry.godaddy
+// Submitted by Rohan Durrant <tldns@registry.godaddy>
+graphic.design
// GoIP DNS Services : http://www.goip.de
// Submitted by Christian Poulter <milchstrasse@goip.de>
goip.de
// Google, Inc.
-// Submitted by Eduardo Vela <evn@google.com>
-*.run.app
-web.app
-*.0emm.com
-appspot.com
-*.r.appspot.com
-codespot.com
-googleapis.com
-googlecode.com
-pagespeedmobilizer.com
-publishproxy.com
-withgoogle.com
-withyoutube.com
-*.gateway.dev
-cloud.goog
-translate.goog
-*.usercontent.goog
-cloudfunctions.net
+// Submitted by Shannon McCabe <public-suffix-editors@google.com>
blogspot.ae
blogspot.al
blogspot.am
+*.hosted.app
+*.run.app
+web.app
+blogspot.com.ar
+blogspot.co.at
+blogspot.com.au
blogspot.ba
blogspot.be
blogspot.bg
blogspot.bj
+blogspot.com.br
+blogspot.com.by
blogspot.ca
blogspot.cf
blogspot.ch
blogspot.cl
-blogspot.co.at
-blogspot.co.id
-blogspot.co.il
-blogspot.co.ke
-blogspot.co.nz
-blogspot.co.uk
-blogspot.co.za
-blogspot.com
-blogspot.com.ar
-blogspot.com.au
-blogspot.com.br
-blogspot.com.by
blogspot.com.co
-blogspot.com.cy
-blogspot.com.ee
-blogspot.com.eg
-blogspot.com.es
-blogspot.com.mt
-blogspot.com.ng
-blogspot.com.tr
-blogspot.com.uy
+*.0emm.com
+appspot.com
+*.r.appspot.com
+blogspot.com
+codespot.com
+googleapis.com
+googlecode.com
+pagespeedmobilizer.com
+publishproxy.com
+withgoogle.com
+withyoutube.com
blogspot.cv
+blogspot.com.cy
blogspot.cz
blogspot.de
+*.gateway.dev
blogspot.dk
+blogspot.com.ee
+blogspot.com.eg
+blogspot.com.es
blogspot.fi
blogspot.fr
+cloud.goog
+translate.goog
+*.usercontent.goog
blogspot.gr
blogspot.hk
blogspot.hr
blogspot.hu
+blogspot.co.id
blogspot.ie
+blogspot.co.il
blogspot.in
blogspot.is
blogspot.it
blogspot.jp
+blogspot.co.ke
blogspot.kr
blogspot.li
blogspot.lt
@@ -13979,10 +14071,14 @@ blogspot.lu
blogspot.md
blogspot.mk
blogspot.mr
+blogspot.com.mt
blogspot.mx
blogspot.my
+cloudfunctions.net
+blogspot.com.ng
blogspot.nl
blogspot.no
+blogspot.co.nz
blogspot.pe
blogspot.pt
blogspot.qa
@@ -13996,21 +14092,34 @@ blogspot.si
blogspot.sk
blogspot.sn
blogspot.td
+blogspot.com.tr
blogspot.tw
blogspot.ug
+blogspot.co.uk
+blogspot.com.uy
blogspot.vn
+blogspot.co.za
// Goupile : https://goupile.fr
// Submitted by Niels Martignene <hello@goupile.fr>
goupile.fr
+// GOV.UK Pay : https://www.payments.service.gov.uk/
+// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk>
+pymnt.uk
+
+// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
+// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk>
+cloudapps.digital
+london.cloudapps.digital
+
// Government of the Netherlands: https://www.government.nl
// Submitted by <domeinnaam@minaz.nl>
gov.nl
-// Group 53, LLC : https://www.group53.com
-// Submitted by Tyler Todd <noc@nova53.net>
-awsmppl.com
+// GrayJay Web Solutions Inc. : https://grayjaysports.ca
+// Submitted by Matt Yamkowy <info@grayjaysports.ca>
+grayjayleagues.com
// GünstigBestellen : https://günstigbestellen.de
// Submitted by Furkan Akkoc <info@hendelzon.de>
@@ -14027,10 +14136,15 @@ caa.li
ua.rs
conf.se
+// Häkkinen.fi
+// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi>
+xn--hkkinen-5wa.fi
+häkkinen.fi
+
// Handshake : https://handshake.org
// Submitted by Mike Damm <md@md.vc>
-hs.zone
hs.run
+hs.zone
// Hashbang : https://hashbang.sh
hashbang.sh
@@ -14040,10 +14154,28 @@ hashbang.sh
hasura.app
hasura-app.io
+// Hatena Co., Ltd. : https://hatena.co.jp
+// Submitted by Masato Nakamura <blog-developers@hatena.ne.jp>
+hatenablog.com
+hatenadiary.com
+hateblo.jp
+hatenablog.jp
+hatenadiary.jp
+hatenadiary.org
+
// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de
// Submitted by Richard Zowalla <mi-admin@hs-heilbronn.de>
pages.it.hs-heilbronn.de
+// HeiyuSpace: https://lazycat.cloud
+// Submitted by Xia Bin <admin@lazycat.cloud>
+heiyu.space
+
+// Helio Networks : https://heliohost.org
+// Submitted by Ben Frede <admin@heliohost.org>
+helioho.st
+heliohost.us
+
// Hepforge : https://www.hepforge.org
// Submitted by David Grellscheid <admin@hepforge.org>
hepforge.org
@@ -14057,7 +14189,6 @@ herokussl.com
// Submitted by Oren Eini <oren@ravendb.net>
ravendb.cloud
ravendb.community
-ravendb.me
development.run
ravendb.run
@@ -14079,7 +14210,6 @@ secaas.hk
// Submitted by Danilo De Franco<info@hoplix.shop>
hoplix.shop
-
// HOSTBIP REGISTRY : https://www.hostbip.com/
// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com>
orx.biz
@@ -14096,19 +14226,30 @@ sch.so
// Submitted by Bohdan Dub <support@hostfly.com.ua>
ie.ua
-// HostyHosting (hostyhosting.com)
+// HostyHosting (https://hostyhosting.com)
hostyhosting.io
-// Häkkinen.fi
-// Submitted by Eero Häkkinen <Eero+psl@Häkkinen.fi>
-xn--hkkinen-5wa.fi
-häkkinen.fi
+// Hypernode B.V. : https://www.hypernode.com/
+// Submitted by Cipriano Groenendal <security@nl.team.blue>
+hypernode.io
+
+// I-O DATA DEVICE, INC. : http://www.iodata.com/
+// Submitted by Yuji Minagawa <domains-admin@iodata.jp>
+iobb.net
+
+// i-registry s.r.o. : http://www.i-registry.cz/
+// Submitted by Martin Semrad <semrad@i-registry.cz>
+co.cz
// Ici la Lune : http://www.icilalune.com/
// Submitted by Simon Morvan <simon@icilalune.com>
*.moonscale.io
moonscale.net
+// iDOT Services Limited : http://www.domain.gr.com
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+gr.com
+
// iki.fi
// Submitted by Hannu Aronsson <haa@iki.fi>
iki.fi
@@ -14118,11 +14259,6 @@ iki.fi
ibxos.it
iliadboxos.it
-// Impertrix Solutions : <https://impertrixcdn.com>
-// Submitted by Zhixiang Zhao <csuite@impertrix.com>
-impertrixcdn.com
-impertrix.com
-
// Incsub, LLC: https://incsub.com/
// Submitted by Aaron Edwards <sysadmins@incsub.com>
smushcdn.com
@@ -14138,10 +14274,10 @@ in-berlin.de
in-brb.de
in-butter.de
in-dsl.de
-in-dsl.net
-in-dsl.org
in-vpn.de
+in-dsl.net
in-vpn.net
+in-dsl.org
in-vpn.org
// info.at : http://www.info.at/
@@ -14149,7 +14285,7 @@ biz.at
info.at
// info.cx : http://info.cx
-// Submitted by Jacob Slater <whois@igloo.to>
+// Submitted by June Slater <whois@igloo.to>
info.cx
// Interlegis : http://www.interlegis.leg.br
@@ -14190,6 +14326,15 @@ pixolino.com
// Submitted by Vasiliy Sheredeko <piphon@gmail.com>
na4u.ru
+// IONOS SE : https://www.ionos.com/,
+// IONOS Group SE: https://www.ionos-group.com/
+// submitted by Henrik Willert <security@ionos.com>
+apps-1and1.com
+live-website.com
+apps-1and1.net
+websitebuilder.online
+app-ionos.space
+
// iopsys software solutions AB : https://iopsys.eu/
// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu>
iopsys.se
@@ -14198,6 +14343,14 @@ iopsys.se
// Submitted by Matthew Hardeman <mhardeman@ipifony.com>
ipifony.net
+// ir.md : https://nic.ir.md
+// Submitted by Ali Soizi <info@nic.ir.md>
+ir.md
+
+// is-a.dev : https://www.is-a.dev
+// Submitted by William Harrison <admin@m.is-a.dev>
+is-a.dev
+
// IServ GmbH : https://iserv.de
// Submitted by Mario Hoberg <info@iserv.de>
iservschule.de
@@ -14207,10 +14360,6 @@ schulserver.de
test-iserv.de
iserv.dev
-// I-O DATA DEVICE, INC. : http://www.iodata.com/
-// Submitted by Yuji Minagawa <domains-admin@iodata.jp>
-iobb.net
-
// Jelastic, Inc. : https://jelastic.com/
// Submitted by Ihor Kolodyuk <ik@jelastic.com>
mel.cloudlets.com.au
@@ -14306,10 +14455,15 @@ myjino.ru
// Submitted by Daniel Fariña <ingenieria@jotelulu.com>
jotelulu.cloud
+// JouwWeb B.V. : https://www.jouwweb.nl
+// Submitted by Camilo Sperberg <tech@webador.com>
+webadorsite.com
+jouwweb.site
+
// Joyent : https://www.joyent.com/
// Submitted by Brian Bennett <brian.bennett@joyent.com>
-*.triton.zone
*.cns.joyent.com
+*.triton.zone
// JS.ORG : http://dns.js.org
// Submitted by Stefan Keim <admin@js.org>
@@ -14328,6 +14482,11 @@ ktistory.com
// Submitted by Tomi Juntunen <erani@kapsi.fi>
kapsi.fi
+// Katholieke Universiteit Leuven: https://www.kuleuven.be
+// Submitted by Abuse KU Leuven <abuse@kuleuven.be>
+ezproxy.kuleuven.be
+kuleuven.cloud
+
// Keyweb AG : https://www.keyweb.de
// Submitted by Martin Dannehl <postmaster@keymachine.de>
keymachine.de
@@ -14345,23 +14504,14 @@ knightpoint.systems
// Submitted by Iván Oliva <ivan.oliva@koobin.com>
koobin.events
-// KUROKU LTD : https://kuroku.ltd/
-// Submitted by DisposaBoy <security@oya.to>
-oya.to
-
-// Katholieke Universiteit Leuven: https://www.kuleuven.be
-// Submitted by Abuse KU Leuven <abuse@kuleuven.be>
-kuleuven.cloud
-ezproxy.kuleuven.be
-
-// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf
-co.krd
-edu.krd
-
// Krellian Ltd. : https://krellian.com
// Submitted by Ben Francis <ben@krellian.com>
-krellian.net
webthings.io
+krellian.net
+
+// KUROKU LTD : https://kuroku.ltd/
+// Submitted by DisposaBoy <security@oya.to>
+oya.to
// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de
// Submitted by Lars Laehn <info@lcube.de>
@@ -14379,6 +14529,14 @@ lpusercontent.com
// Submitted by Lelux Admin <publisuffix@lelux.site>
lelux.site
+// libp2p project : https://libp2p.io
+// Submitted by Interplanetary Shipyard <psl@ipshipyard.com>
+libp2p.direct
+
+// Libre IT Ltd : https://libre.nz
+// Submitted by Tomas Maggio <support@libre.nz>
+runcontainers.dev
+
// Lifetime Hosting : https://Lifetime.Hosting/
// Submitted by Mike Fillator <support@lifetime.hosting>
co.business
@@ -14389,14 +14547,10 @@ co.network
co.place
co.technology
-// Lightmaker Property Manager, Inc. : https://app.lmpm.com/
-// Submitted by Greg Holland <greg.holland@lmpm.com>
-app.lmpm.com
-
// linkyard ldt: https://www.linkyard.ch/
// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch>
-linkyard.cloud
linkyard-cloud.ch
+linkyard.cloud
// Linode : https://linode.com
// Submitted by <security@linode.com>
@@ -14409,6 +14563,11 @@ ip.linodeusercontent.com
// Submitted by Victor Velchev <admin@liquidnetlimited.com>
we.bs
+// Listen53 : https://www.l53.net
+// Submitted by Gerry Keh <biz@l53.net>
+filegear-sg.me
+ggff.net
+
// Localcert : https://localcert.dev
// Submitted by Lann Martin <security@localcert.dev>
*.user.localcert.dev
@@ -14425,14 +14584,14 @@ loginline.io
loginline.services
loginline.site
-// Lokalized : https://lokalized.nl
-// Submitted by Noah Taheij <noah@lokalized.nl>
-servers.run
-
// Lõhmus Family, The
// Submitted by Heiki Lõhmus <hostmaster at lohmus dot me>
lohmus.me
+// Lokalized : https://lokalized.nl
+// Submitted by Noah Taheij <noah@lokalized.nl>
+servers.run
+
// LubMAN UMCS Sp. z o.o : https://lubman.pl/
// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl>
krasnik.pl
@@ -14451,18 +14610,19 @@ lugs.org.uk
// Lukanet Ltd : https://lukanet.com
// Submitted by Anton Avramov <register@lukanet.com>
barsy.bg
-barsy.co.uk
-barsyonline.co.uk
+barsy.club
barsycenter.com
barsyonline.com
-barsy.club
barsy.de
+barsy.dev
barsy.eu
+barsy.gr
barsy.in
barsy.info
barsy.io
barsy.me
barsy.menu
+barsyonline.menu
barsy.mobi
barsy.net
barsy.online
@@ -14470,42 +14630,43 @@ barsy.org
barsy.pro
barsy.pub
barsy.ro
+barsy.rs
barsy.shop
+barsyonline.shop
barsy.site
+barsy.store
barsy.support
barsy.uk
+barsy.co.uk
+barsyonline.co.uk
// Magento Commerce
// Submitted by Damien Tournoud <dtournoud@magento.cloud>
*.magentosite.cloud
-// May First - People Link : https://mayfirst.org/
-// Submitted by Jamie McClelland <info@mayfirst.org>
-mayfirst.info
-mayfirst.org
-
// Mail.Ru Group : https://hb.cldmail.ru
// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru>
hb.cldmail.ru
-// Mail Transfer Platform : https://www.neupeer.com
-// Submitted by Li Hui <lihui@neupeer.com>
-cn.vu
+// May First - People Link : https://mayfirst.org/
+// Submitted by Jamie McClelland <info@mayfirst.org>
+mayfirst.info
+mayfirst.org
// Maze Play: https://www.mazeplay.com
// Submitted by Adam Humpherys <adam@mws.dev>
mazeplay.com
-// mcpe.me : https://mcpe.me
-// Submitted by Noa Heyl <hi@noa.dev>
-mcpe.me
-
// McHost : https://mchost.ru
// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru>
mcdir.me
mcdir.ru
-mcpre.ru
vps.mcdir.ru
+mcpre.ru
+
+// mcpe.me : https://mcpe.me
+// Submitted by Noa Heyl <hi@noa.dev>
+mcpe.me
// Mediatech : https://mediatech.by
// Submitted by Evgeniy Kozhuhovskiy <ugenk@mediatech.by>
@@ -14553,12 +14714,10 @@ co.pl
// Managed by Corporate Domains
// Microsoft Azure : https://home.azure
*.azurecontainer.io
-*.cloudapp.azure.com
azure-api.net
+azure-mobile.net
azureedge.net
azurefd.net
-azurewebsites.net
-azure-mobile.net
azurestaticapps.net
1.azurestaticapps.net
2.azurestaticapps.net
@@ -14572,26 +14731,27 @@ eastasia.azurestaticapps.net
eastus2.azurestaticapps.net
westeurope.azurestaticapps.net
westus2.azurestaticapps.net
+azurewebsites.net
cloudapp.net
trafficmanager.net
blob.core.windows.net
servicebus.windows.net
+// MikroTik: https://mikrotik.com
+// Submitted by MikroTik SysAdmin Team <support@mikrotik.com>
+sn.mynetname.net
+
// minion.systems : http://minion.systems
// Submitted by Robert Böttinger <r@minion.systems>
csx.cc
-// Mintere : https://mintere.com/
-// Submitted by Ben Aubin <security@mintere.com>
-mintere.site
-
// MobileEducation, LLC : https://joinforte.com
// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
forte.id
-// Mozilla Corporation : https://mozilla.com
-// Submitted by Ben Francis <bfrancis@mozilla.com>
-mozilla-iot.org
+// MODX Systems LLC : https://modx.com
+// Submitted by Elizabeth Southwell <elizabeth@modx.com>
+modx.dev
// Mozilla Foundation : https://mozilla.org/
// Submitted by glob <glob@mozilla.com>
@@ -14606,8 +14766,8 @@ pp.ru
// Mythic Beasts : https://www.mythic-beasts.com
// Submitted by Paul Cammish <kelduum@mythic-beasts.com>
hostedpi.com
-customer.mythic-beasts.com
caracal.mythic-beasts.com
+customer.mythic-beasts.com
fentiger.mythic-beasts.com
lynx.mythic-beasts.com
ocelot.mythic-beasts.com
@@ -14627,6 +14787,10 @@ ui.nabu.casa
// Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de>
cloud.nospamproxy.com
+// Netfy Domains : https://netfy.domains
+// Submitted by Suranga Ranasinghe <security@mavicsoft.com>
+netfy.app
+
// Netlify : https://www.netlify.com
// Submitted by Jessica Parsons <jessica@netlify.com>
netlify.app
@@ -14635,6 +14799,18 @@ netlify.app
// Submitted by Trung Tran <Trung.Tran@neustar.biz>
4u.com
+// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
+// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net>
+nfshost.com
+
+// NFT.Storage : https://nft.storage/
+// Submitted by Vasco Santos <vasco.santos@protocol.ai> or <support@nft.storage>
+ipfs.nftstorage.link
+
+// NGO.US Registry : https://nic.ngo.us
+// Submitted by Alstra Solutions Ltd. Networking Team <admin@alstra.org>
+ngo.us
+
// ngrok : https://ngrok.com/
// Submitted by Alan Shreve <alan@ngrok.com>
ngrok.app
@@ -14650,17 +14826,116 @@ jp.ngrok.io
sa.ngrok.io
us.ngrok.io
ngrok.pizza
+ngrok.pro
// Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl)
torun.pl
// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/
-// Submitted by Nicholas Ford <nick@nimbushosting.co.uk>
+// Submitted by Nicholas Ford <dev@nimbushosting.co.uk>
nh-serv.co.uk
+nimsite.uk
-// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
-// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net>
-nfshost.com
+// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+ar.com
+hu.com
+kr.com
+no.com
+qc.com
+uy.com
+
+// No-IP.com : https://noip.com/
+// Submitted by Deven Reza <publicsuffixlist@noip.com>
+mmafan.biz
+myftp.biz
+no-ip.biz
+no-ip.ca
+fantasyleague.cc
+gotdns.ch
+3utilities.com
+blogsyte.com
+ciscofreak.com
+damnserver.com
+ddnsking.com
+ditchyourip.com
+dnsiskinky.com
+dynns.com
+geekgalaxy.com
+health-carereform.com
+homesecuritymac.com
+homesecuritypc.com
+myactivedirectory.com
+mysecuritycamera.com
+myvnc.com
+net-freaks.com
+onthewifi.com
+point2this.com
+quicksytes.com
+securitytactics.com
+servebeer.com
+servecounterstrike.com
+serveexchange.com
+serveftp.com
+servegame.com
+servehalflife.com
+servehttp.com
+servehumour.com
+serveirc.com
+servemp3.com
+servep2p.com
+servepics.com
+servequake.com
+servesarcasm.com
+stufftoread.com
+unusualperson.com
+workisboring.com
+dvrcam.info
+ilovecollege.info
+no-ip.info
+brasilia.me
+ddns.me
+dnsfor.me
+hopto.me
+loginto.me
+noip.me
+webhop.me
+bounceme.net
+ddns.net
+eating-organic.net
+mydissent.net
+myeffect.net
+mymediapc.net
+mypsx.net
+mysecuritycamera.net
+nhlfan.net
+no-ip.net
+pgafan.net
+privatizehealthinsurance.net
+redirectme.net
+serveblog.net
+serveminecraft.net
+sytes.net
+cable-modem.org
+collegefan.org
+couchpotatofries.org
+hopto.org
+mlbfan.org
+myftp.org
+mysecuritycamera.org
+nflfan.org
+no-ip.org
+read-books.org
+ufcfan.org
+zapto.org
+no-ip.co.uk
+golffan.us
+noip.us
+pointto.us
+
+// NodeArt : https://nodeart.io
+// Submitted by Konstantin Nosov <Nosov@nodeart.io>
+stage.nodeart.io
// Noop : https://noop.app
// Submitted by Nathaniel Schweinberg <noop@rearc.io>
@@ -14679,6 +14954,10 @@ noop.app
// Submitted by Laurent Pellegrino <security@noticeable.io>
noticeable.news
+// Notion Labs, Inc : https://www.notion.so/
+// Submitted by Jess Yao <trust-core-team@makenotion.com>
+notion.site
+
// Now-DNS : https://now-dns.com
// Submitted by Steve Russell <steve@now-dns.com>
dnsking.ch
@@ -14712,114 +14991,23 @@ zapto.xyz
nsupdate.info
nerdpol.ovh
-// No-IP.com : https://noip.com/
-// Submitted by Deven Reza <publicsuffixlist@noip.com>
-blogsyte.com
-brasilia.me
-cable-modem.org
-ciscofreak.com
-collegefan.org
-couchpotatofries.org
-damnserver.com
-ddns.me
-ditchyourip.com
-dnsfor.me
-dnsiskinky.com
-dvrcam.info
-dynns.com
-eating-organic.net
-fantasyleague.cc
-geekgalaxy.com
-golffan.us
-health-carereform.com
-homesecuritymac.com
-homesecuritypc.com
-hopto.me
-ilovecollege.info
-loginto.me
-mlbfan.org
-mmafan.biz
-myactivedirectory.com
-mydissent.net
-myeffect.net
-mymediapc.net
-mypsx.net
-mysecuritycamera.com
-mysecuritycamera.net
-mysecuritycamera.org
-net-freaks.com
-nflfan.org
-nhlfan.net
-no-ip.ca
-no-ip.co.uk
-no-ip.net
-noip.us
-onthewifi.com
-pgafan.net
-point2this.com
-pointto.us
-privatizehealthinsurance.net
-quicksytes.com
-read-books.org
-securitytactics.com
-serveexchange.com
-servehumour.com
-servep2p.com
-servesarcasm.com
-stufftoread.com
-ufcfan.org
-unusualperson.com
-workisboring.com
-3utilities.com
-bounceme.net
-ddns.net
-ddnsking.com
-gotdns.ch
-hopto.org
-myftp.biz
-myftp.org
-myvnc.com
-no-ip.biz
-no-ip.info
-no-ip.org
-noip.me
-redirectme.net
-servebeer.com
-serveblog.net
-servecounterstrike.com
-serveftp.com
-servegame.com
-servehalflife.com
-servehttp.com
-serveirc.com
-serveminecraft.net
-servemp3.com
-servepics.com
-servequake.com
-sytes.net
-webhop.me
-zapto.org
-
-// NodeArt : https://nodeart.io
-// Submitted by Konstantin Nosov <Nosov@nodeart.io>
-stage.nodeart.io
-
-// Nucleos Inc. : https://nucleos.com
-// Submitted by Piotr Zduniak <piotr@nucleos.com>
-pcloud.host
-
// NYC.mn : http://www.information.nyc.mn
// Submitted by Matthew Brown <mattbrown@nyc.mn>
nyc.mn
+// O3O.Foundation : https://o3o.foundation/
+// Submitted by the prvcy.page Registry Team <psl@registry.prvcy.page>
+prvcy.page
+
+// Obl.ong : <https://obl.ong>
+// Submitted by Reese Armstrong <team@obl.ong>
+obl.ong
+
// Observable, Inc. : https://observablehq.com
// Submitted by Mike Bostock <dns@observablehq.com>
+observablehq.cloud
static.observableusercontent.com
-// Octopodal Solutions, LLC. : https://ulterius.io/
-// Submitted by Andrew Sampson <andrew@ulterius.io>
-cya.gg
-
// OMG.LOL : <https://omg.lol>
// Submitted by Adam Newbold <adam@omg.lol>
omg.lol
@@ -14834,30 +15022,33 @@ omniwe.site
// One.com: https://www.one.com/
// Submitted by Jacob Bunk Nielsen <jbn@one.com>
-123hjemmeside.dk
-123hjemmeside.no
-123homepage.it
-123kotisivu.fi
-123minsida.se
-123miweb.es
-123paginaweb.pt
-123sait.ru
-123siteweb.fr
123webseite.at
-123webseite.de
123website.be
+simplesite.com.br
123website.ch
+simplesite.com
+123webseite.de
+123hjemmeside.dk
+123miweb.es
+123kotisivu.fi
+123siteweb.fr
+simplesite.gr
+123homepage.it
123website.lu
123website.nl
+123hjemmeside.no
service.one
-simplesite.com
-simplesite.com.br
-simplesite.gr
simplesite.pl
+123paginaweb.pt
+123minsida.se
-// One Fold Media : http://www.onefoldmedia.com/
-// Submitted by Eddie Jones <eddie@onefoldmedia.com>
-nid.io
+// Open Domains : https://open-domains.net
+// Submitted by William Harrison <admin@open-domains.net>
+is-a-fullstack.dev
+is-cool.dev
+is-not-a.dev
+localplayer.dev
+is-local.org
// Open Social : https://www.getopensocial.com/
// Submitted by Alexander Varwijk <security@getopensocial.com>
@@ -14879,6 +15070,11 @@ operaunite.com
// Submitted by Alexandre Linte <alexandre.linte@orange.com>
tech.orange
+// OsSav Technology Ltd. : https://ossav.com/
+// TLD Nic: http://nic.can.re - TLD Whois Server: whois.can.re
+// Submitted by OsSav Technology Ltd. <support@ossav.com>
+can.re
+
// Oursky Limited : https://authgear.com/, https://skygear.io/
// Submitted by Authgear Team <hello@authgear.com>, Skygear Developer <hello@skygear.io>
authgear-staging.com
@@ -14891,8 +15087,8 @@ outsystemscloud.com
// OVHcloud: https://ovhcloud.com
// Submitted by Vincent Cassé <vincent.casse@ovhcloud.com>
-*.webpaas.ovh.net
*.hosting.ovh.net
+*.webpaas.ovh.net
// OwnProvider GmbH: http://www.ownprovider.com
// Submitted by Jan Moennich <jan.moennich@ownprovider.com>
@@ -14915,37 +15111,31 @@ oy.lc
// Submitted by Derek Myers <derek@pagefog.com>
pgfog.com
-// Pagefront : https://www.pagefronthq.com/
-// Submitted by Jason Kriss <jason@pagefronthq.com>
-pagefrontapp.com
-
// PageXL : https://pagexl.com
// Submitted by Yann Guichard <yann@pagexl.com>
pagexl.com
+// Pantheon Systems, Inc. : https://pantheon.io/
+// Submitted by Gary Dylina <gary@pantheon.io>
+gotpantheon.com
+pantheonsite.io
+
// Paywhirl, Inc : https://paywhirl.com/
// Submitted by Daniel Netzer <dan@paywhirl.com>
*.paywhirl.com
// pcarrier.ca Software Inc: https://pcarrier.ca/
// Submitted by Pierre Carrier <pc@rrier.ca>
-bar0.net
-bar1.net
-bar2.net
-rdv.to
-
-// .pl domains (grandfathered)
-art.pl
-gliwice.pl
-krakow.pl
-poznan.pl
-wroc.pl
-zakopane.pl
+*.xmit.co
+xmit.dev
+madethis.site
+srv.us
+gh.srv.us
+gl.srv.us
-// Pantheon Systems, Inc. : https://pantheon.io/
-// Submitted by Gary Dylina <gary@pantheon.io>
-pantheonsite.io
-gotpantheon.com
+// PE Ulyanov Kirill Sergeevich : https://airy.host
+// Submitted by Kirill Ulyanov <k.ulyanov@airy.host>
+lk3.ru
// Peplink | Pepwave : http://peplink.com/
// Submitted by Steve Leung <steveleung@peplink.com>
@@ -14955,10 +15145,6 @@ mypep.link
// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com>
perspecta.cloud
-// PE Ulyanov Kirill Sergeevich : https://airy.host
-// Submitted by Kirill Ulyanov <k.ulyanov@airy.host>
-lk3.ru
-
// Planet-Work : https://www.planet-work.com/
// Submitted by Frédéric VANNIÈRE <f.vanniere@planet-work.com>
on-web.fr
@@ -14979,12 +15165,6 @@ platter-app.com
platter-app.dev
platterp.us
-// Plesk : https://www.plesk.com/
-// Submitted by Anton Akhtyamov <program-managers@plesk.com>
-pdns.page
-plesk.page
-pleskns.com
-
// Pley AB : https://www.pley.com/
// Submitted by Henning Pohl <infra@pley.com>
pley.games
@@ -15008,8 +15188,8 @@ pstmn.io
mock.pstmn.io
httpbin.org
-//prequalifyme.today : https://prequalifyme.today
-//Submitted by DeepakTiwari deepak@ivylead.io
+// prequalifyme.today : https://prequalifyme.today
+// Submitted by DeepakTiwari deepak@ivylead.io
prequalifyme.today
// prgmr.com : https://prgmr.com/
@@ -15020,10 +15200,6 @@ xen.prgmr.com
// Submitted by registry <lendl@nic.at>
priv.at
-// privacytools.io : https://www.privacytools.io/
-// Submitted by Jonah Aragon <jonah@privacytools.io>
-prvcy.page
-
// Protocol Labs : https://protocol.ai/
// Submitted by Michael Burns <noc@protocol.ai>
*.dweb.link
@@ -15046,6 +15222,24 @@ pubtls.org
pythonanywhere.com
eu.pythonanywhere.com
+// QA2
+// Submitted by Daniel Dent (https://www.danieldent.com/)
+qa2.com
+
+// QCX
+// Submitted by Cassandra Beelen <cassandra@beelen.one>
+qcx.io
+*.sys.qcx.io
+
+// QNAP System Inc : https://www.qnap.com
+// Submitted by Nick Chang <cloudadmin@qnap.com>
+myqnapcloud.cn
+alpha-myqnapcloud.com
+dev-myqnapcloud.com
+mycloudnas.com
+mynascloud.com
+myqnapcloud.com
+
// QOTO, Org.
// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org>
qoto.io
@@ -15062,37 +15256,6 @@ ladesk.com
// Submitted by Dani Biro <dani@pymet.com>
qbuser.com
-// Rad Web Hosting: https://radwebhosting.com
-// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
-cloudsite.builders
-
-// Redgate Software: https://red-gate.com
-// Submitted by Andrew Farries <andrew.farries@red-gate.com>
-instances.spawn.cc
-
-// Redstar Consultants : https://www.redstarconsultants.com/
-// Submitted by Jons Slemmer <jons@redstarconsultants.com>
-instantcloud.cn
-
-// Russian Academy of Sciences
-// Submitted by Tech Support <support@rasnet.ru>
-ras.ru
-
-// QA2
-// Submitted by Daniel Dent (https://www.danieldent.com/)
-qa2.com
-
-// QCX
-// Submitted by Cassandra Beelen <cassandra@beelen.one>
-qcx.io
-*.sys.qcx.io
-
-// QNAP System Inc : https://www.qnap.com
-// Submitted by Nick Chang <nickchang@qnap.com>
-dev-myqnapcloud.com
-alpha-myqnapcloud.com
-myqnapcloud.com
-
// Quip : https://quip.com
// Submitted by Patrick Linehan <plinehan@quip.com>
*.quipelements.com
@@ -15107,16 +15270,32 @@ vaporcloud.io
rackmaze.com
rackmaze.net
-// Rakuten Games, Inc : https://dev.viberplay.io
-// Submitted by Joshua Zhang <public-suffix@rgames.jp>
-g.vbrplsbx.io
+// Rad Web Hosting: https://radwebhosting.com
+// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
+cloudsite.builders
+myradweb.net
+servername.us
+
+// Radix FZC : http://domains.in.net
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+web.in
+in.net
+
+// Raidboxes GmbH : https://raidboxes.de
+// Submitted by Auke Tembrink <hostmaster@raidboxes.de>
+myrdbx.io
+site.rb-hosting.io
// Rancher Labs, Inc : https://rancher.com
// Submitted by Vincent Fiduccia <domains@rancher.com>
-*.on-k3s.io
*.on-rancher.cloud
+*.on-k3s.io
*.on-rio.io
+// RavPage : https://www.ravpage.co.il
+// Submitted by Roni Horowitz <roni@responder.co.il>
+ravpage.co.il
+
// Read The Docs, Inc : https://www.readthedocs.org
// Submitted by David Fischer <team@readthedocs.org>
readthedocs.io
@@ -15125,17 +15304,50 @@ readthedocs.io
// Submitted by Tim Kramer <tkramer@rhcloud.com>
rhcloud.com
+// Redgate Software: https://red-gate.com
+// Submitted by Andrew Farries <andrew.farries@red-gate.com>
+instances.spawn.cc
+
// Render : https://render.com
// Submitted by Anurag Goel <dev@render.com>
-app.render.com
onrender.com
+app.render.com
// Repl.it : https://repl.it
-// Submitted by Lincoln Bergeson <lincoln@replit.com>
+// Submitted by Lincoln Bergeson <psl@repl.it>
+replit.app
+id.replit.app
firewalledreplit.co
id.firewalledreplit.co
repl.co
id.repl.co
+replit.dev
+archer.replit.dev
+bones.replit.dev
+canary.replit.dev
+global.replit.dev
+hacker.replit.dev
+id.replit.dev
+janeway.replit.dev
+kim.replit.dev
+kira.replit.dev
+kirk.replit.dev
+odo.replit.dev
+paris.replit.dev
+picard.replit.dev
+pike.replit.dev
+prerelease.replit.dev
+reed.replit.dev
+riker.replit.dev
+sisko.replit.dev
+spock.replit.dev
+staging.replit.dev
+sulu.replit.dev
+tarpit.replit.dev
+teams.replit.dev
+tucker.replit.dev
+wesley.replit.dev
+worf.replit.dev
repl.run
// Resin.io : https://resin.io
@@ -15160,6 +15372,11 @@ adimo.co.uk
// Submitted by Micah Anderson <micah@riseup.net>
itcouldbewor.se
+// Roar Domains LLC : https://roar.basketball/
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+aus.basketball
+nz.basketball
+
// Rochester Institute of Technology : http://www.rit.edu/
// Submitted by Jennifer Herting <jchits@rit.edu>
git-pages.rit.edu
@@ -15191,6 +15408,10 @@ xn--90a1af.xn--p1acf
xn--41a.xn--p1acf
я.рус
+// Russian Academy of Sciences
+// Submitted by Tech Support <support@rasnet.ru>
+ras.ru
+
// SAKURA Internet Inc. : https://www.sakura.ad.jp/
// Submitted by Internet Service Department <rs-vendor-ml@sakura.ad.jp>
180r.com
@@ -15242,11 +15463,19 @@ from.tv
sakura.tv
// Salesforce.com, Inc. https://salesforce.com/
-// Submitted by Michael Biven <mbiven@salesforce.com> and Aaron Romeo <aaron.romeo@salesforce.com>
+// Submitted by Salesforce Public Suffix List Team <public-suffix-list@salesforce.com>
*.builder.code.com
*.dev-builder.code.com
*.stg-builder.code.com
*.001.test.code-builder-stg.platform.salesforce.com
+*.d.crm.dev
+*.w.crm.dev
+*.wa.crm.dev
+*.wb.crm.dev
+*.wc.crm.dev
+*.wd.crm.dev
+*.we.crm.dev
+*.wf.crm.dev
// Sandstorm Development Group, Inc. : https://sandcats.io/
// Submitted by Asheesh Laroia <asheesh@sandstorm.io>
@@ -15254,8 +15483,8 @@ sandcats.io
// SBE network solutions GmbH : https://www.sbe.de/
// Submitted by Norman Meilick <nm@sbe.de>
-logoip.de
logoip.com
+logoip.de
// Scaleway : https://www.scaleway.com/
// Submitted by Rémy Léone <rleone@scaleway.com>
@@ -15301,6 +15530,10 @@ service.gov.scot
// Submitted by Shante Adam <shante@skyhat.io>
scrysec.com
+// Scrypted : https://scrypted.app
+// Submitted by Koushik Dutta <public-suffix-list@scrypted.app>
+client.scrypted.io
+
// Securepoint GmbH : https://www.securepoint.de
// Submitted by Erik Anders <erik.anders@securepoint.de>
firewall-gateway.com
@@ -15322,13 +15555,17 @@ seidat.net
// Submitted by Yuriy Romadin <contact@sellfy.com>
sellfy.store
+// Sendmsg: https://www.sendmsg.co.il
+// Submitted by Assaf Stern <domains@comstar.co.il>
+minisite.ms
+
// Senseering GmbH : https://www.senseering.de
// Submitted by Felix Mönckemeyer <f.moenckemeyer@senseering.de>
senseering.net
-// Sendmsg: https://www.sendmsg.co.il
-// Submitted by Assaf Stern <domains@comstar.co.il>
-minisite.ms
+// Servebolt AS: https://servebolt.com
+// Submitted by Daniel Kjeserud <cloudops@servebolt.com>
+servebolt.cloud
// Service Magnet : https://myservicemagnet.com
// Submitted by Dave Sanders <dave@myservicemagnet.com>
@@ -15340,10 +15577,13 @@ biz.ua
co.ua
pp.ua
-// Shift Crypto AG : https://shiftcrypto.ch
-// Submitted by alex <alex@shiftcrypto.ch>
-shiftcrypto.dev
-shiftcrypto.io
+// Shanghai Accounting Society : https://www.sasf.org.cn
+// Submitted by Information Administration <info@sasf.org.cn>
+as.sh.cn
+
+// Sheezy.Art : https://sheezy.art
+// Submitted by Nyoom <admin@sheezy.art>
+sheezy.games
// ShiftEdit : https://shiftedit.net/
// Submitted by Adam Jimenez <adam@shiftcreate.com>
@@ -15387,6 +15627,10 @@ bounty-full.com
alpha.bounty-full.com
beta.bounty-full.com
+// Small Technology Foundation : https://small-tech.org
+// Submitted by Aral Balkan <aral@small-tech.org>
+small-web.org
+
// Smallregistry by Promopixel SARL: https://www.smallregistry.net
// Former AFNIC's SLDs
// Submitted by Jérôme Lipowicz <support@promopixel.com>
@@ -15401,18 +15645,14 @@ pharmacien.fr
port.fr
veterinaire.fr
-// Small Technology Foundation : https://small-tech.org
-// Submitted by Aral Balkan <aral@small-tech.org>
-small-web.org
-
// Smoove.io : https://www.smoove.io/
// Submitted by Dan Kozak <dan@smoove.io>
vp4.me
// Snowflake Inc : https://www.snowflake.com/
-// Submitted by Faith Olapade <faith.olapade@snowflake.com>
-snowflake.app
-privatelink.snowflake.app
+// Submitted by Sam Haar <psl@snowflake.com>
+*.snowflake.app
+*.privatelink.snowflake.app
streamlit.app
streamlitapp.com
@@ -15420,41 +15660,18 @@ streamlitapp.com
// Submitted by Ian Streeter <ian@snowplowanalytics.com>
try-snowplow.com
-// SourceHut : https://sourcehut.org
-// Submitted by Drew DeVault <sir@cmpwn.com>
-srht.site
-
-// Stackhero : https://www.stackhero.io
-// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
-stackhero-network.com
-
-// Staclar : https://staclar.com
-// Submitted by Q Misell <q@staclar.com>
-musician.io
-// Submitted by Matthias Merkel <matthias.merkel@staclar.com>
-novecore.site
-
-// staticland : https://static.land
-// Submitted by Seth Vincent <sethvincent@gmail.com>
-static.land
-dev.static.land
-sites.static.land
-
-// Storebase : https://www.storebase.io
-// Submitted by Tony Schirmer <tony@storebase.io>
-storebase.store
-
-// Strategic System Consulting (eApps Hosting): https://www.eapps.com/
-// Submitted by Alex Oancea <aoancea@cloudscale365.com>
-vps-host.net
-atl.jelastic.vps-host.net
-njs.jelastic.vps-host.net
-ric.jelastic.vps-host.net
+// Software Consulting Michal Zalewski : https://www.mafelo.com
+// Submitted by Michal Zalewski <security@mafelo.com>
+mafelo.net
// Sony Interactive Entertainment LLC : https://sie.com/
// Submitted by David Coles <david.coles@sony.com>
playstation-cloud.com
+// SourceHut : https://sourcehut.org
+// Submitted by Drew DeVault <sir@cmpwn.com>
+srht.site
+
// SourceLair PC : https://www.sourcelair.com
// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com>
apps.lair.io
@@ -15464,6 +15681,10 @@ apps.lair.io
// Submitted by Reza Akhavan <spacekit.io@gmail.com>
spacekit.io
+// SparrowHost : https://sparrowhost.in/
+// Submitted by Anant Pandey <info@sparrowhost.in>
+ind.mom
+
// SpeedPartner GmbH: https://www.speedpartner.de/
// Submitted by Stefan Neufeind <info@speedpartner.de>
customer.speedpartner.de
@@ -15490,10 +15711,51 @@ myspreadshop.pl
myspreadshop.se
myspreadshop.co.uk
+// StackBlitz : https://stackblitz.com
+// Submitted by Dominic Elm <hello@stackblitz.com>
+w-corp-staticblitz.com
+w-credentialless-staticblitz.com
+w-staticblitz.com
+
+// Stackhero : https://www.stackhero.io
+// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
+stackhero-network.com
+
+// STACKIT : https://www.stackit.de/en/
+// Submitted by STACKIT-DNS Team (Simon Stier) <stackit-dns@mail.schwarz>
+runs.onstackit.cloud
+stackit.gg
+stackit.rocks
+stackit.run
+stackit.zone
+
+// Staclar : https://staclar.com
+// Submitted by Q Misell <q@staclar.com>
+// Submitted by Matthias Merkel <matthias.merkel@staclar.com>
+musician.io
+novecore.site
+
// Standard Library : https://stdlib.com
// Submitted by Jacob Lee <jacob@stdlib.com>
api.stdlib.com
+// stereosense GmbH : https://www.involve.me
+// Submitted by Florian Burmann <publicsuffix@involve.me>
+feedback.ac
+forms.ac
+assessments.cx
+calculators.cx
+funnels.cx
+paynow.cx
+quizzes.cx
+researched.cx
+tests.cx
+surveys.so
+
+// Storebase : https://www.storebase.io
+// Submitted by Tony Schirmer <tony@storebase.io>
+storebase.store
+
// Storipress : https://storipress.com
// Submitted by Benno Liu <benno@storipress.com>
storipress.app
@@ -15502,15 +15764,33 @@ storipress.app
// Submitted by Philip Hutchins <hostmaster@storj.io>
storj.farm
-// Studenten Net Twente : http://www.snt.utwente.nl/
-// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
-utwente.io
+// Strapi : https://strapi.io/
+// Submitted by Florent Baldino <security@strapi.io>
+strapiapp.com
+media.strapiapp.com
+
+// Strategic System Consulting (eApps Hosting): https://www.eapps.com/
+// Submitted by Alex Oancea <aoancea@cloudscale365.com>
+vps-host.net
+atl.jelastic.vps-host.net
+njs.jelastic.vps-host.net
+ric.jelastic.vps-host.net
+
+// Streak : https://streak.com
+// Submitted by Blake Kadatz <eng@streak.com>
+streak-link.com
+streaklinks.com
+streakusercontent.com
// Student-Run Computing Facility : https://www.srcf.net/
// Submitted by Edwin Balani <sysadmins@srcf.net>
soc.srcf.net
user.srcf.net
+// Studenten Net Twente : http://www.snt.utwente.nl/
+// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
+utwente.io
+
// Sub 6 Limited: http://www.sub6.com
// Submitted by Dan Miller <dm@sub6.com>
temp-dns.com
@@ -15520,12 +15800,11 @@ temp-dns.com
supabase.co
supabase.in
supabase.net
-su.paba.se
// Symfony, SAS : https://symfony.com/
// Submitted by Fabien Potencier <fabien@symfony.com>
-*.s5y.io
*.sensiosite.cloud
+*.s5y.io
// Syncloud : https://syncloud.org
// Submitted by Boris Rybalkin <syncloud@syncloud.it>
@@ -15547,14 +15826,14 @@ dsmynas.net
familyds.net
dsmynas.org
familyds.org
-vpnplus.to
direct.quickconnect.to
+vpnplus.to
// Tabit Technologies Ltd. : https://tabit.cloud/
// Submitted by Oren Agiv <oren@tabit.cloud>
-tabitorder.co.il
-mytabit.co.il
mytabit.com
+mytabit.co.il
+tabitorder.co.il
// TAIFUN Software AG : http://taifun-software.de
// Submitted by Bjoern Henke <dev-server@taifun-software.de>
@@ -15564,14 +15843,20 @@ taifun-dns.de
// Submitted by David Anderson <danderson@tailscale.com>
beta.tailscale.net
ts.net
+*.c.ts.net
-// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+// TASK geographical domains (https://www.task.gda.pl/uslugi/dns)
gda.pl
gdansk.pl
gdynia.pl
med.pl
sopot.pl
+// tawk.to, Inc : https://www.tawk.to
+// Submitted by tawk.to developer team <dev-accounts@tawk.to>
+p.tawk.email
+p.tawkto.email
+
// team.blue https://team.blue
// Submitted by Cedric Dubois <cedric.dubois@team.blue>
site.tb-hosting.com
@@ -15594,11 +15879,11 @@ telebit.io
reservd.com
thingdustdata.com
cust.dev.thingdust.io
+reservd.dev.thingdust.io
cust.disrec.thingdust.io
+reservd.disrec.thingdust.io
cust.prod.thingdust.io
cust.testing.thingdust.io
-reservd.dev.thingdust.io
-reservd.disrec.thingdust.io
reservd.testing.thingdust.io
// ticket i/O GmbH : https://ticket.io
@@ -15612,7 +15897,7 @@ azimuth.network
tlon.network
// Tor Project, Inc. : https://torproject.org
-// Submitted by Antoine Beaupré <anarcat@torproject.org
+// Submitted by Antoine Beaupré <anarcat@torproject.org>
torproject.net
pages.torproject.net
@@ -15660,8 +15945,6 @@ tuxfamily.org
// TwoDNS : https://www.twodns.de/
// Submitted by TwoDNS-Support <support@two-dns.de>
dd-dns.de
-diskstation.eu
-diskstation.org
dray-dns.de
draydns.de
dyn-vpn.de
@@ -15672,6 +15955,8 @@ my-wan.de
syno-ds.de
synology-diskstation.de
synology-ds.de
+diskstation.eu
+diskstation.org
// Typedream : https://typedream.com
// Submitted by Putri Karunia <putri@typedream.com>
@@ -15683,20 +15968,29 @@ pro.typeform.com
// Uberspace : https://uberspace.de
// Submitted by Moritz Werner <mwerner@jonaspasche.com>
-uber.space
*.uberspace.de
+uber.space
// UDR Limited : http://www.udr.hk.com
// Submitted by registry <hostmaster@udr.hk.com>
hk.com
-hk.org
-ltd.hk
inc.hk
+ltd.hk
+hk.org
// UK Intis Telecom LTD : https://it.com
// Submitted by ITComdomains <to@it.com>
it.com
+// Unison Computing, PBC : https://unison.cloud
+// Submitted by Simon Højberg <security@unison.cloud>
+unison-services.cloud
+
+// United Gameserver GmbH : https://united-gameserver.de
+// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
+virtual-user.de
+virtualuser.de
+
// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/
// see also: whois -h whois.udr.org.yt help
// Submitted by Atanunu Igbunuroghene <publicsuffixlist@udr.org.yt>
@@ -15706,10 +16000,14 @@ biz.wf
sch.wf
org.yt
-// United Gameserver GmbH : https://united-gameserver.de
-// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
-virtualuser.de
-virtual-user.de
+// University of Banja Luka : https://unibl.org
+// Domains for Republic of Srpska administrative entity.
+// Submitted by Marko Ivanovic <kormang@hotmail.rs>
+rs.ba
+
+// University of Bielsko-Biala regional domain: http://dns.bielsko.pl/
+// Submitted by Marcin <dns@ath.bielsko.pl>
+bielsko.pl
// Upli : https://upli.io
// Submitted by Lenny Bakkalian <lenny.bakkalian@gmail.com>
@@ -15720,13 +16018,18 @@ upli.io
urown.cloud
dnsupdate.info
-// .US
-// Submitted by Ed Moore <Ed.Moore@lib.de.us>
-lib.de.us
+// US REGISTRY LLC : http://us.org
+// Submitted by Gavin Brown <gavin.brown@centralnic.com>
+us.org
-// VeryPositive SIA : http://very.lv
-// Submitted by Danko Aleksejevs <danko@very.lv>
-2038.io
+// V.UA Domain Administrator : https://domain.v.ua/
+// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua>
+v.ua
+
+// Val Town, Inc : https://val.town/
+// Submitted by Tom MacWright <security@val.town>
+express.val.run
+web.val.run
// Vercel, Inc : https://vercel.com/
// Submitted by Connor Davis <security@vercel.com>
@@ -15734,6 +16037,10 @@ vercel.app
vercel.dev
now.sh
+// VeryPositive SIA : http://very.lv
+// Submitted by Danko Aleksejevs <danko@very.lv>
+2038.io
+
// Viprinet Europe GmbH : http://www.viprinet.com
// Submitted by Simon Kissel <hostmaster@viprinet.com>
router.management
@@ -15746,51 +16053,6 @@ v-info.info
// Submitted by Nathan van Bakel <info@voorloper.com>
voorloper.cloud
-// Voxel.sh DNS : https://voxel.sh/dns/
-// Submitted by Mia Rehlinger <dns@voxel.sh>
-neko.am
-nyaa.am
-be.ax
-cat.ax
-es.ax
-eu.ax
-gg.ax
-mc.ax
-us.ax
-xy.ax
-nl.ci
-xx.gl
-app.gp
-blog.gt
-de.gt
-to.gt
-be.gy
-cc.hn
-io.kg
-jp.kg
-tv.kg
-uk.kg
-us.kg
-de.ls
-at.md
-de.md
-jp.md
-to.md
-indie.porn
-vxl.sh
-ch.tc
-me.tc
-we.tc
-nyan.to
-at.vg
-blog.vu
-dev.vu
-me.vu
-
-// V.UA Domain Administrator : https://domain.v.ua/
-// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua>
-v.ua
-
// Vultr Objects : https://www.vultr.com/products/object-storage/
// Submitted by Niels Maumenee <storage@vultr.com>
*.vultrobjects.com
@@ -15799,42 +16061,70 @@ v.ua
// Submitted by Masayuki Note <masa@blade.wafflecell.com>
wafflecell.com
+// Webflow, Inc. : https://www.webflow.com
+// Submitted by Webflow Security Team <security@webflow.com>
+webflow.io
+webflowtest.io
+
// WebHare bv: https://www.webhare.com/
// Submitted by Arnold Hendriks <info@webhare.com>
*.webhare.dev
// WebHotelier Technologies Ltd: https://www.webhotelier.net/
// Submitted by Apostolos Tsakpinis <apostolos.tsakpinis@gmail.com>
-reserve-online.net
-reserve-online.com
bookonline.app
hotelwithflight.com
+reserve-online.com
+reserve-online.net
-// WeDeploy by Liferay, Inc. : https://www.wedeploy.com
-// Submitted by Henrique Vicente <security@wedeploy.com>
-wedeploy.io
-wedeploy.me
-wedeploy.sh
+// WebPros International, LLC : https://webpros.com/
+// Submitted by Nicolas Rochelemagne <public.suffix@webpros.com>
+cprapid.com
+pleskns.com
+wp2.host
+pdns.page
+plesk.page
+wpsquared.site
+
+// WebWaddle Ltd: https://webwaddle.com/
+// Submitted by Merlin Glander <hostmaster@webwaddle.com>
+*.wadl.top
// Western Digital Technologies, Inc : https://www.wdc.com
// Submitted by Jung Jin <jungseok.jin@wdc.com>
remotewd.com
+// Whatbox Inc. : https://whatbox.ca/
+// Submitted by Anthony Ryan <servers@whatbox.ca>
+box.ca
+
// WIARD Enterprises : https://wiardweb.com
// Submitted by Kidd Hustle <kiddhustle@wiardweb.com>
pages.wiardweb.com
// Wikimedia Labs : https://wikitech.wikimedia.org
// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org>
-wmflabs.org
toolforge.org
wmcloud.org
+wmflabs.org
+
+// William Harrison : https://wdh.gg
+// Submitted by William Harrison <domains@wdh.gg>
+*.wdh.app
// WISP : https://wisp.gg
// Submitted by Stepan Fedotov <stepan@wisp.gg>
panel.gg
daemon.panel.gg
+// Wix.com, Inc. : https://www.wix.com
+// Submitted by Shahar Talmi / Alon Kochba <publicsuffixlist@wix.com>
+wixsite.com
+wixstudio.com
+editorx.io
+wixstudio.io
+wix.run
+
// Wizard Zines : https://wizardzines.com
// Submitted by Julia Evans <julia@wizardzines.com>
messwithdns.com
@@ -15860,13 +16150,6 @@ weeklylottery.org.uk
wpenginepowered.com
js.wpenginepowered.com
-// Wix.com, Inc. : https://www.wix.com
-// Submitted by Shahar Talmi <shahar@wix.com>
-wixsite.com
-editorx.io
-wixstudio.io
-wix.run
-
// XenonCloud GbR: https://xenoncloud.net
// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net>
half.host
@@ -15922,6 +16205,10 @@ za.org
// Submitted by Julian Alker <security@zap-hosting.com>
zap.cloud
+// Zeabur : https://zeabur.com/
+// Submitted by Zeabur Team <contact@zeabur.com>
+zeabur.app
+
// Zine EOOD : https://zine.bg/
// Submitted by Martin Angelov <martin@zine.bg>
bss.design
diff --git a/contrib/publicsuffix/idn.pl b/contrib/publicsuffix/idn.pl
index dd46d652a..dd46d652a 100644..100755
--- a/contrib/publicsuffix/idn.pl
+++ b/contrib/publicsuffix/idn.pl
diff --git a/contrib/zstd/CMakeLists.txt b/contrib/zstd/CMakeLists.txt
index 4601e53ce..9fba28fa4 100644
--- a/contrib/zstd/CMakeLists.txt
+++ b/contrib/zstd/CMakeLists.txt
@@ -24,4 +24,4 @@ SET(ZSTDSRC
zstd_opt.c)
ADD_LIBRARY(rspamd-zstd STATIC ${ZSTDSRC})
-ADD_DEFINITIONS(-DZSTD_DISABLE_ASM) \ No newline at end of file
+ADD_DEFINITIONS(-DZSTD_DISABLE_ASM -DZSTD_DISABLE_DEPRECATE_WARNINGS) \ No newline at end of file
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 000000000..2f256afcb
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,84 @@
+import globals from "globals";
+import js from "@eslint/js";
+import stylistic from "@stylistic/eslint-plugin";
+
+export default [
+ js.configs.all,
+ stylistic.configs["all-flat"],
+ {ignores: ["interface/js/lib/"]},
+ {
+ languageOptions: {
+ ecmaVersion: 2016,
+ globals: {
+ ...globals.browser,
+ define: false,
+ },
+ sourceType: "script",
+ },
+ plugins: {
+ "@stylistic": stylistic,
+ },
+ rules: {
+ "@stylistic/array-bracket-newline": ["error", "consistent"],
+ "@stylistic/array-element-newline": "off",
+ "@stylistic/brace-style": ["error", "1tbs", {allowSingleLine: true}],
+ "@stylistic/comma-dangle": ["error", "only-multiline"],
+ "@stylistic/dot-location": ["error", "property"],
+ "@stylistic/function-call-argument-newline": "off",
+ "@stylistic/function-paren-newline": "off",
+ "@stylistic/indent-binary-ops": "off",
+ "@stylistic/max-len": ["error", {code: 128}],
+ "@stylistic/max-statements-per-line": ["error", {max: 2}],
+ "@stylistic/multiline-comment-style": "off",
+ "@stylistic/multiline-ternary": ["error", "always-multiline"],
+ "@stylistic/newline-per-chained-call": ["error", {ignoreChainWithDepth: 5}],
+ "@stylistic/no-extra-parens": ["error", "functions"],
+ "@stylistic/object-property-newline": ["error", {allowAllPropertiesOnSameLine: true}],
+ "@stylistic/padded-blocks": ["error", "never"],
+ "@stylistic/quote-props": ["error", "consistent-as-needed"],
+ "@stylistic/quotes": ["error", "double", {avoidEscape: true}],
+ "@stylistic/semi": ["error", "always"],
+ "@stylistic/space-before-function-paren": ["error", {
+ anonymous: "always",
+ named: "never",
+ }],
+
+ "camelcase": "off",
+ "capitalized-comments": "off",
+ "curly": ["error", "multi-line"],
+ "func-names": "off",
+ "func-style": ["error", "declaration"],
+ "id-length": ["error", {min: 1}],
+ "line-comment-position": "off",
+ "logical-assignment-operators": ["error", "never"],
+ "max-lines": "off",
+ "max-lines-per-function": "off",
+ "max-params": ["warn", 6],
+ "max-statements": ["warn", 55],
+ "multiline-comment-style": "off",
+ "no-continue": "off",
+ "no-inline-comments": "off",
+ "no-invalid-this": "off",
+ "no-magic-numbers": "off",
+ "no-negated-condition": "off",
+ "no-plusplus": "off",
+ "no-ternary": "off",
+ "no-unused-vars": ["error", {caughtErrors: "none"}],
+ "object-shorthand": "off",
+ "one-var": ["error", {initialized: "never"}],
+ "prefer-named-capture-group": "off",
+ "prefer-object-has-own": "off",
+ "prefer-spread": "off",
+ "prefer-template": "off",
+ "require-unicode-regexp": "off",
+ "sort-keys": "off",
+ },
+ },
+ {
+ files: ["**/*.mjs"],
+ languageOptions: {ecmaVersion: 2020, sourceType: "module"},
+ rules: {
+ "sort-keys": "error",
+ },
+ },
+];
diff --git a/interface/js/app/libft.js b/interface/js/app/libft.js
index 1e81cfd26..84ee2667e 100644
--- a/interface/js/app/libft.js
+++ b/interface/js/app/libft.js
@@ -249,7 +249,7 @@ define(["jquery", "app/common", "footable"],
});
/* eslint-enable no-underscore-dangle */
- /* eslint-disable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
+ /* eslint-disable consistent-this, no-underscore-dangle */
FooTable.actionFilter = FooTable.Filtering.extend({
construct: function (instance) {
this._super(instance);
@@ -304,6 +304,7 @@ define(["jquery", "app/common", "footable"],
const selected = self.$action.val();
if (selected !== self.def) {
const not = self.$not.is(":checked");
+ // eslint-disable-next-line no-useless-assignment
let query = null;
if (selected === "reject") {
@@ -319,7 +320,7 @@ define(["jquery", "app/common", "footable"],
self.filter();
}
});
- /* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
+ /* eslint-enable consistent-this, no-underscore-dangle */
const columns = (table in columnsCustom)
? columnsDefault.map((column) => $.extend({}, column, columnsCustom[table][column.name]))
@@ -600,6 +601,7 @@ define(["jquery", "app/common", "footable"],
item.id = item["message-id"];
if (table === "history") {
+ // eslint-disable-next-line no-useless-assignment
let rcpt = {};
if (!item.rcpt_mime.length) {
rcpt = format_rcpt(true, false);
diff --git a/interface/js/app/stats.js b/interface/js/app/stats.js
index 04b4a75c5..8ac4eacdd 100644
--- a/interface/js/app/stats.js
+++ b/interface/js/app/stats.js
@@ -35,6 +35,7 @@ define(["jquery", "app/common", "d3pie", "d3"],
const hours = seconds % 31536000 % 2628000 % 86400 / 3600 >> 0;
const minutes = seconds % 31536000 % 2628000 % 86400 % 3600 / 60 >> 0;
/* eslint-enable no-bitwise */
+ // eslint-disable-next-line no-useless-assignment
let out = null;
if (years > 0) {
if (months > 0) {
diff --git a/interface/js/app/symbols.js b/interface/js/app/symbols.js
index a720a696d..3ff5d5a4b 100644
--- a/interface/js/app/symbols.js
+++ b/interface/js/app/symbols.js
@@ -130,7 +130,7 @@ define(["jquery", "app/common", "footable"],
const [{data}] = json;
const items = process_symbols_data(data);
- /* eslint-disable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
+ /* eslint-disable consistent-this, no-underscore-dangle */
FooTable.groupFilter = FooTable.Filtering.extend({
construct: function (instance) {
this._super(instance);
@@ -183,7 +183,7 @@ define(["jquery", "app/common", "footable"],
}
}
});
- /* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
+ /* eslint-enable consistent-this, no-underscore-dangle */
common.tables.symbols = FooTable.init("#symbolsTable", {
breakpoints: common.breakpoints,
diff --git a/lualib/lua_bayes_learn.lua b/lualib/lua_bayes_learn.lua
index ea97db6f8..82f044d7d 100644
--- a/lualib/lua_bayes_learn.lua
+++ b/lualib/lua_bayes_learn.lua
@@ -18,6 +18,7 @@ limitations under the License.
local lua_util = require "lua_util"
local lua_verdict = require "lua_verdict"
+local logger = require "rspamd_logger"
local N = "lua_bayes"
local exports = {}
@@ -56,16 +57,16 @@ exports.autolearn = function(task, conf)
local mime_rcpts = 'undef'
local mr = task:get_recipients('mime')
if mr then
+ local r_addrs = {}
for _, r in ipairs(mr) do
- if mime_rcpts == 'undef' then
- mime_rcpts = r.addr
- else
- mime_rcpts = mime_rcpts .. ',' .. r.addr
- end
+ r_addrs[#r_addrs + 1] = r.addr
+ end
+ if #r_addrs > 0 then
+ mime_rcpts = table.concat(r_addrs, ',')
end
end
- lua_util.debugm(N, task, 'id: %s, from: <%s>: can autolearn %s: score %s %s %s, mime_rcpts: <%s>',
+ logger.info(task, 'id: %s, from: <%s>: can autolearn %s: score %s %s %s, mime_rcpts: <%s>',
task:get_header('Message-Id') or '<undef>',
from and from[1].addr or 'undef',
verdict,
diff --git a/lualib/lua_cfg_transform.lua b/lualib/lua_cfg_transform.lua
index 02d8526e5..265ca34c0 100644
--- a/lualib/lua_cfg_transform.lua
+++ b/lualib/lua_cfg_transform.lua
@@ -17,102 +17,165 @@ limitations under the License.
local logger = require "rspamd_logger"
local lua_util = require "lua_util"
local rspamd_util = require "rspamd_util"
-local fun = require "fun"
-local function is_implicit(t)
- local mt = getmetatable(t)
+-- Converts surbl module config to rbl module
+local function surbl_section_convert(cfg, section)
+ local rbl_section = cfg.rbl.rbls
+ local wl = section.whitelist
+ if section.rules then
+ for name, value in section.rules:pairs() do
+ if rbl_section[name] then
+ logger.warnx(rspamd_config, 'conflicting names in surbl and rbl rules: %s, prefer surbl rule!',
+ name)
+ end
+ local converted = {
+ urls = true,
+ ignore_defaults = true,
+ }
- return mt and mt.class and mt.class == 'ucl.type.impl_array'
-end
+ if wl then
+ converted.whitelist = wl
+ end
-local function metric_pairs(t)
- -- collect the keys
- local keys = {}
- local implicit_array = is_implicit(t)
-
- local function gen_keys(tbl)
- if implicit_array then
- for _, v in ipairs(tbl) do
- if v.name then
- table.insert(keys, { v.name, v })
- v.name = nil
- else
- -- Very tricky to distinguish:
- -- group {name = "foo" ... } + group "blah" { ... }
- for gr_name, gr in pairs(v) do
- if type(gr_name) ~= 'number' then
- -- We can also have implicit arrays here
- local gr_implicit = is_implicit(gr)
-
- if gr_implicit then
- for _, gr_elt in ipairs(gr) do
- table.insert(keys, { gr_name, gr_elt })
- end
- else
- table.insert(keys, { gr_name, gr })
- end
- end
- end
+ for k, v in value:pairs() do
+ local skip = false
+ -- Rename
+ if k == 'suffix' then
+ k = 'rbl'
end
- end
- else
- if tbl.name then
- table.insert(keys, { tbl.name, tbl })
- tbl.name = nil
- else
- for k, v in pairs(tbl) do
- if type(k) ~= 'number' then
- -- We can also have implicit arrays here
- local sym_implicit = is_implicit(v)
-
- if sym_implicit then
- for _, elt in ipairs(v) do
- table.insert(keys, { k, elt })
- end
- else
- table.insert(keys, { k, v })
- end
+ if k == 'ips' then
+ k = 'returncodes'
+ end
+ if k == 'bits' then
+ k = 'returnbits'
+ end
+ if k == 'noip' then
+ k = 'no_ip'
+ end
+ -- Crappy legacy
+ if k == 'options' then
+ if v == 'noip' or v == 'no_ip' then
+ converted.no_ip = true
+ skip = true
end
end
+ if k:match('check_') then
+ local n = k:match('check_(.*)')
+ k = n
+ end
+
+ if k == 'dkim' and v then
+ converted.dkim_domainonly = false
+ converted.dkim_match_from = true
+ end
+
+ if k == 'emails' and v then
+ -- To match surbl behaviour
+ converted.emails_domainonly = true
+ end
+
+ if not skip then
+ converted[k] = lua_util.deepcopy(v)
+ end
end
+ rbl_section[name] = lua_util.override_defaults(rbl_section[name], converted)
end
end
+end
+
- gen_keys(t)
+-- Converts surbl module config to rbl module
+local function emails_section_convert(cfg, section)
+ local rbl_section = cfg.rbl.rbls
+ local wl = section.whitelist
+ if section.rules then
+ for name, value in section.rules:pairs() do
+ if rbl_section[name] then
+ logger.warnx(rspamd_config, 'conflicting names in emails and rbl rules: %s, prefer emails rule!',
+ name)
+ end
+ local converted = {
+ emails = true,
+ ignore_defaults = true,
+ }
- -- return the iterator function
- local i = 0
- return function()
- i = i + 1
- if keys[i] then
- return keys[i][1], keys[i][2]
+ if wl then
+ converted.whitelist = wl
+ end
+
+ for k, v in value:pairs() do
+ local skip = false
+ -- Rename
+ if k == 'dnsbl' then
+ k = 'rbl'
+ end
+ if k == 'check_replyto' then
+ k = 'replyto'
+ end
+ if k == 'hashlen' then
+ k = 'hash_len'
+ end
+ if k == 'encoding' then
+ k = 'hash_format'
+ end
+ if k == 'domain_only' then
+ k = 'emails_domainonly'
+ end
+ if k == 'delimiter' then
+ k = 'emails_delimiter'
+ end
+ if k == 'skip_body' then
+ skip = true
+ if v then
+ -- Hack
+ converted.emails = false
+ converted.replyto = true
+ else
+ converted.emails = true
+ end
+ end
+ if k == 'expect_ip' then
+ -- Another stupid hack
+ if not converted.return_codes then
+ converted.returncodes = {}
+ end
+ local symbol = value.symbol or name
+ converted.returncodes[symbol] = { v }
+ skip = true
+ end
+
+ if not skip then
+ converted[k] = lua_util.deepcopy(v)
+ end
+ end
+ rbl_section[name] = lua_util.override_defaults(rbl_section[name], converted)
end
end
end
local function group_transform(cfg, k, v)
- if v.name then
- k = v.name
+ if v:at('name') then
+ k = v:at('name'):unwrap()
end
local new_group = {
symbols = {}
}
- if v.enabled then
- new_group.enabled = v.enabled
+ if v:at('enabled') then
+ new_group.enabled = v:at('enabled'):unwrap()
end
- if v.disabled then
- new_group.disabled = v.disabled
+ if v:at('disabled') then
+ new_group.disabled = v:at('disabled'):unwrap()
end
if v.max_score then
- new_group.max_score = v.max_score
+ new_group.max_score = v:at('max_score'):unwrap()
end
- if v.symbol then
- for sk, sv in metric_pairs(v.symbol) do
- if sv.name then
- sk = sv.name
+ if v:at('symbol') then
+ for sk, sv in v:at('symbol'):pairs() do
+ if sv:at('name') then
+ sk = sv:at('name'):unwrap()
sv.name = nil -- Remove field
end
@@ -120,26 +183,28 @@ local function group_transform(cfg, k, v)
end
end
- if not cfg.group then
+ if not cfg:at('group') then
cfg.group = {}
end
- if cfg.group[k] then
- cfg.group[k] = lua_util.override_defaults(cfg.group[k], new_group)
+ if cfg:at('group'):at(k) then
+ cfg:at('group')[k] = lua_util.override_defaults(cfg:at('group')[k]:unwrap(), new_group)
else
- cfg.group[k] = new_group
+ cfg:at('group')[k] = new_group
end
logger.infox("overriding group %s from the legacy metric settings", k)
end
local function symbol_transform(cfg, k, v)
+ local groups = cfg:at('group')
-- first try to find any group where there is a definition of this symbol
- for gr_n, gr in pairs(cfg.group) do
- if gr.symbols and gr.symbols[k] then
+ for gr_n, gr in groups:pairs() do
+ local symbols = gr:at('symbols')
+ if symbols and symbols:at(k) then
-- We override group symbol with ungrouped symbol
logger.infox("overriding group symbol %s in the group %s", k, gr_n)
- gr.symbols[k] = lua_util.override_defaults(gr.symbols[k], v)
+ symbols[k] = lua_util.override_defaults(symbols:at(k):unwrap(), v:unwrap())
return
end
end
@@ -148,86 +213,52 @@ local function symbol_transform(cfg, k, v)
if not sym or not sym.group then
-- Otherwise we just use group 'ungrouped'
- if not cfg.group.ungrouped then
- cfg.group.ungrouped = {
- symbols = {}
+ if not groups:at('ungrouped') then
+ groups.ungrouped = {
+ symbols = {
+ [k] = v
+ }
}
+ else
+ groups:at('ungrouped'):at('symbols')[k] = v
end
- cfg.group.ungrouped.symbols[k] = v
logger.debugx("adding symbol %s to the group 'ungrouped'", k)
end
end
-local function test_groups(groups)
- for gr_name, gr in pairs(groups) do
- if not gr.symbols then
- local cnt = 0
- for _, _ in pairs(gr) do
- cnt = cnt + 1
- end
-
- if cnt == 0 then
- logger.debugx('group %s is empty', gr_name)
- else
- logger.infox('group %s has no symbols', gr_name)
- end
- end
+local function convert_metric(cfg, metric)
+ if metric:type() ~= 'object' then
+ logger.errx('invalid metric definition: %s', metric)
+ return
end
-end
-local function convert_metric(cfg, metric)
- if metric.actions then
- cfg.actions = lua_util.override_defaults(cfg.actions, metric.actions)
+ if metric:at('actions') then
+ local existing_actions = cfg:at('actions') and cfg:at('actions'):unwrap() or {}
+ cfg.actions = lua_util.override_defaults(existing_actions, metric:at('actions'):unwrap())
logger.infox("overriding actions from the legacy metric settings")
end
- if metric.unknown_weight then
- cfg.actions.unknown_weight = metric.unknown_weight
+ if metric:at('unknown_weight') then
+ logger.infox("overriding unknown weight from the legacy metric settings")
+ cfg:at('actions').unknown_weight = metric:at('unknown_weight'):unwrap()
end
- if metric.subject then
+ if metric:at('subject') then
logger.infox("overriding subject from the legacy metric settings")
- cfg.actions.subject = metric.subject
+ cfg:at('actions').subject = metric:at('subject'):unwrap()
end
- if metric.group then
- for k, v in metric_pairs(metric.group) do
+ if metric:at('group') then
+ for k, v in metric:at('group'):pairs() do
group_transform(cfg, k, v)
end
- else
- if not cfg.group then
- cfg.group = {
- ungrouped = {
- symbols = {}
- }
- }
- end
end
- if metric.symbol then
- for k, v in metric_pairs(metric.symbol) do
+ if metric:at('symbol') then
+ for k, v in metric:at('symbol'):pairs() do
symbol_transform(cfg, k, v)
end
end
-
- return cfg
-end
-
--- Converts a table of groups indexed by number (implicit array) to a
--- merged group definition
-local function merge_groups(groups)
- local ret = {}
- for k, gr in pairs(groups) do
- if type(k) == 'number' then
- for key, sec in pairs(gr) do
- ret[key] = sec
- end
- else
- ret[k] = gr
- end
- end
-
- return ret
end
-- Checks configuration files for statistics
@@ -245,155 +276,37 @@ local function check_statistics_sanity()
end
end
--- Converts surbl module config to rbl module
-local function surbl_section_convert(cfg, section)
- local rbl_section = cfg.rbl.rbls
- local wl = section.whitelist
- for name, value in pairs(section.rules or {}) do
- if rbl_section[name] then
- logger.warnx(rspamd_config, 'conflicting names in surbl and rbl rules: %s, prefer surbl rule!',
- name)
- end
- local converted = {
- urls = true,
- ignore_defaults = true,
- }
-
- if wl then
- converted.whitelist = wl
- end
-
- for k, v in pairs(value) do
- local skip = false
- -- Rename
- if k == 'suffix' then
- k = 'rbl'
- end
- if k == 'ips' then
- k = 'returncodes'
- end
- if k == 'bits' then
- k = 'returnbits'
- end
- if k == 'noip' then
- k = 'no_ip'
- end
- -- Crappy legacy
- if k == 'options' then
- if v == 'noip' or v == 'no_ip' then
- converted.no_ip = true
- skip = true
- end
- end
- if k:match('check_') then
- local n = k:match('check_(.*)')
- k = n
- end
-
- if k == 'dkim' and v then
- converted.dkim_domainonly = false
- converted.dkim_match_from = true
- end
-
- if k == 'emails' and v then
- -- To match surbl behaviour
- converted.emails_domainonly = true
- end
-
- if not skip then
- converted[k] = lua_util.deepcopy(v)
- end
- end
- rbl_section[name] = lua_util.override_defaults(rbl_section[name], converted)
- end
-end
-
--- Converts surbl module config to rbl module
-local function emails_section_convert(cfg, section)
- local rbl_section = cfg.rbl.rbls
- local wl = section.whitelist
- for name, value in pairs(section.rules or {}) do
- if rbl_section[name] then
- logger.warnx(rspamd_config, 'conflicting names in emails and rbl rules: %s, prefer emails rule!',
- name)
- end
- local converted = {
- emails = true,
- ignore_defaults = true,
- }
+return function(cfg)
+ local ret = false
- if wl then
- converted.whitelist = wl
- end
+ if cfg:at('metric') then
+ local metric = cfg:at('metric')
- for k, v in pairs(value) do
- local skip = false
- -- Rename
- if k == 'dnsbl' then
- k = 'rbl'
- end
- if k == 'check_replyto' then
- k = 'replyto'
- end
- if k == 'hashlen' then
- k = 'hash_len'
- end
- if k == 'encoding' then
- k = 'hash_format'
- end
- if k == 'domain_only' then
- k = 'emails_domainonly'
- end
- if k == 'delimiter' then
- k = 'emails_delimiter'
- end
- if k == 'skip_body' then
- skip = true
- if v then
- -- Hack
- converted.emails = false
- converted.replyto = true
- else
- converted.emails = true
- end
- end
- if k == 'expect_ip' then
- -- Another stupid hack
- if not converted.return_codes then
- converted.returncodes = {}
+ -- There are two things that we can have (old `metric_pairs` logic)
+ -- 1. A metric is a single metric definition like: metric { name = "default", ... }
+ -- 2. A metric is a list of metrics like: metric { "default": ... }
+ if metric:at('actions') or metric:at('name') then
+ convert_metric(cfg, metric)
+ else
+ for _, v in cfg:at('metric'):pairs() do
+ if v:type() == 'object' then
+ logger.infox('converting metric element %s', v)
+ convert_metric(cfg, v)
end
- local symbol = value.symbol or name
- converted.returncodes[symbol] = { v }
- skip = true
- end
-
- if not skip then
- converted[k] = lua_util.deepcopy(v)
end
end
- rbl_section[name] = lua_util.override_defaults(rbl_section[name], converted)
- end
-end
-
-return function(cfg)
- local ret = false
-
- if cfg['metric'] then
- for _, v in metric_pairs(cfg.metric) do
- cfg = convert_metric(cfg, v)
- end
ret = true
end
- if cfg.symbols then
- for k, v in metric_pairs(cfg.symbols) do
+ if cfg:at('symbols') then
+ for k, v in cfg:at('symbols'):pairs() do
symbol_transform(cfg, k, v)
end
end
check_statistics_sanity()
- if not cfg.actions then
+ if not cfg:at('actions') then
logger.errx('no actions defined')
else
-- Perform sanity check for actions
@@ -402,24 +315,26 @@ return function(cfg)
'rewrite subject', 'rewrite_subject', 'quarantine',
'reject', 'discard' }
- if not cfg.actions['no action'] and not cfg.actions['no_action'] and
- not cfg.actions['accept'] then
+ local actions = cfg:at('actions')
+ if not actions:at('no action') and not actions:at('no_action') and
+ not actions:at('accept') then
for _, d in ipairs(actions_defs) do
- if cfg.actions[d] then
-
- local action_score = nil
- if type(cfg.actions[d]) == 'number' then
- action_score = cfg.actions[d]
- elseif type(cfg.actions[d]) == 'table' and cfg.actions[d]['score'] then
- action_score = cfg.actions[d]['score']
+ if actions:at(d) then
+
+ local action_score
+ local act = actions:at(d)
+ if act:type() ~= 'object' then
+ action_score = act:unwrap()
+ elseif act:type() == 'object' and act:at('score') then
+ action_score = act:at('score'):unwrap()
end
- if type(cfg.actions[d]) ~= 'table' and not action_score then
- cfg.actions[d] = nil
+ if act:type() ~= 'object' and not action_score then
+ actions[d] = nil
elseif type(action_score) == 'number' and action_score < 0 then
- cfg.actions['no_action'] = cfg.actions[d] - 0.001
+ actions['no_action'] = actions:at(d):unwrap() - 0.001
logger.infox(rspamd_config, 'set no_action score to: %s, as action %s has negative score',
- cfg.actions['no_action'], d)
+ actions:at('no_action'):unwrap(), d)
break
end
end
@@ -433,7 +348,7 @@ return function(cfg)
actions_set['grow_factor'] = true
actions_set['subject'] = true
- for k, _ in pairs(cfg.actions) do
+ for k, _ in cfg:at('actions'):pairs() do
if not actions_set[k] then
logger.warnx(rspamd_config, 'unknown element in actions section: %s', k)
end
@@ -452,13 +367,13 @@ return function(cfg)
for i = 1, (#actions_order - 1) do
local act = actions_order[i]
- if cfg.actions[act] and type(cfg.actions[act]) == 'number' then
- local score = cfg.actions[act]
+ if actions:at(act) and actions:at(act):type() ~= 'object' then
+ local score = actions:at(act):unwrap()
for j = i + 1, #actions_order do
local next_act = actions_order[j]
- if cfg.actions[next_act] and type(cfg.actions[next_act]) == 'number' then
- local next_score = cfg.actions[next_act]
+ if actions:at(next_act) and actions:at(next_act):type() == 'number' then
+ local next_score = actions:at(next_act):unwrap()
if next_score <= score then
logger.errx(rspamd_config, 'invalid actions thresholds order: action %s (%s) must have lower ' ..
'score than action %s (%s)', act, score, next_act, next_score)
@@ -470,15 +385,18 @@ return function(cfg)
end
end
- if not cfg.group then
- logger.errx('no symbol groups defined')
- else
- if cfg.group[1] then
- -- We need to merge groups
- cfg.group = merge_groups(cfg.group)
- ret = true
+ -- DKIM signing/ARC legacy
+ for _, mod in ipairs({ 'dkim_signing', 'arc' }) do
+ if cfg:at(mod) then
+ if cfg:at(mod):at('auth_only') then
+ if cfg:at(mod):at('sign_authenticated') then
+ logger.warnx(rspamd_config,
+ 'both auth_only (%s) and sign_authenticated (%s) for %s are specified, prefer auth_only',
+ cfg:at(mod):at('auth_only'):unwrap(), cfg:at(mod):at('sign_authenticated'):unwrap(), mod)
+ end
+ cfg:at(mod).sign_authenticated = cfg:at(mod):at('auth_only')
+ end
end
- test_groups(cfg.group)
end
-- Deal with dkim settings
@@ -504,101 +422,22 @@ return function(cfg)
end
end
- -- Again: legacy stuff :(
- if not cfg.dkim.sign_headers then
- local sec = cfg.dkim_signing
- if sec and sec[1] then
- sec = cfg.dkim_signing[1]
- end
-
- if sec and sec.sign_headers then
- cfg.dkim.sign_headers = sec.sign_headers
- end
- end
-
- -- DKIM signing/ARC legacy
- for _, mod in ipairs({ 'dkim_signing', 'arc' }) do
- if cfg[mod] then
- if cfg[mod].auth_only ~= nil then
- if cfg[mod].sign_authenticated ~= nil then
- logger.warnx(rspamd_config,
- 'both auth_only (%s) and sign_authenticated (%s) for %s are specified, prefer auth_only',
- cfg[mod].auth_only, cfg[mod].sign_authenticated, mod)
- end
- cfg[mod].sign_authenticated = cfg[mod].auth_only
- end
- end
- end
-
- if cfg.dkim and cfg.dkim.sign_headers and type(cfg.dkim.sign_headers) == 'table' then
- -- Flatten
- cfg.dkim.sign_headers = table.concat(cfg.dkim.sign_headers, ':')
- end
-
-- Try to find some obvious issues with configuration
- for k, v in pairs(cfg) do
- if type(v) == 'table' and v[k] and type(v[k]) == 'table' then
+ for k, v in cfg:pairs() do
+ if v:type() == 'object' and v:at(k) and v:at(k):type() == 'object' then
logger.errx('nested section: %s { %s { ... } }, it is likely a configuration error',
k, k)
end
end
-- If neural network is enabled we MUST have `check_all_filters` flag
- if cfg.neural then
- if not cfg.options then
- cfg.options = {}
- end
-
- if not cfg.options.check_all_filters then
- logger.infox(rspamd_config, 'enable `options.check_all_filters` for neural network')
- cfg.options.check_all_filters = true
- end
- end
-
- -- Deal with IP_SCORE
- if cfg.ip_score and (cfg.ip_score.servers or cfg.redis.servers) then
- logger.warnx(rspamd_config, 'ip_score module is deprecated in honor of reputation module!')
-
- if not cfg.reputation then
- cfg.reputation = {
- rules = {}
- }
- end
+ if cfg:at('neural') then
- if not cfg.reputation.rules then
- cfg.reputation.rules = {}
- end
-
- if not fun.any(function(_, v)
- return v.selector and v.selector.ip
- end,
- cfg.reputation.rules) then
- logger.infox(rspamd_config, 'attach ip reputation element to use it')
-
- cfg.reputation.rules.ip_score = {
- selector = {
- ip = {},
- },
- backend = {
- redis = {},
- }
- }
-
- if cfg.ip_score.servers then
- cfg.reputation.rules.ip_score.backend.redis.servers = cfg.ip_score.servers
+ if cfg:at('options') then
+ if not cfg:at('options'):at('check_all_filters') then
+ logger.infox(rspamd_config, 'enable `options.check_all_filters` for neural network')
+ cfg:at('options')['check_all_filters'] = true
end
-
- if cfg.symbols and cfg.symbols['IP_SCORE'] then
- local t = cfg.symbols['IP_SCORE']
-
- if not cfg.symbols['SENDER_REP_SPAM'] then
- cfg.symbols['SENDER_REP_SPAM'] = t
- cfg.symbols['SENDER_REP_HAM'] = t
- cfg.symbols['SENDER_REP_HAM'].weight = -(t.weight or 0)
- end
- end
- else
- logger.infox(rspamd_config, 'ip reputation already exists, do not do any IP_SCORE transforms')
end
end
@@ -613,7 +452,7 @@ return function(cfg)
end
surbl_section_convert(cfg, cfg.surbl)
logger.infox(rspamd_config, 'converted surbl rules to rbl rules')
- cfg.surbl = {}
+ cfg.surbl = nil
end
if cfg.emails then
@@ -627,7 +466,7 @@ return function(cfg)
end
emails_section_convert(cfg, cfg.emails)
logger.infox(rspamd_config, 'converted emails rules to rbl rules')
- cfg.emails = {}
+ cfg.emails = nil
end
-- Common misprint options.upstreams -> options.upstream
diff --git a/lualib/lua_maps.lua b/lualib/lua_maps.lua
index a912039be..2699ea214 100644
--- a/lualib/lua_maps.lua
+++ b/lualib/lua_maps.lua
@@ -97,18 +97,6 @@ local external_map_schema = ts.shape {
local rspamd_http = require "rspamd_http"
local ucl = require "ucl"
-local function url_encode_string(str)
- str = string.gsub(str, "([^%w _%%%-%.~])",
- function(c)
- return string.format("%%%02X", string.byte(c))
- end)
- str = string.gsub(str, " ", "+")
- return str
-end
-
-assert(url_encode_string('上海+中國') == '%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B')
-assert(url_encode_string('? and the Mysterians') == '%3F+and+the+Mysterians')
-
local function query_external_map(map_config, upstreams, key, callback, task)
local http_method = (map_config.method == 'body' or map_config.method == 'form') and 'POST' or 'GET'
local upstream = upstreams:get_upstream_round_robin()
@@ -127,7 +115,7 @@ local function query_external_map(map_config, upstreams, key, callback, task)
key = key
}
elseif map_config.method == 'query' then
- url = string.format('%s?key=%s', url, url_encode_string(tostring(key)))
+ url = string.format('%s?key=%s', url, lua_util.url_encode_string(tostring(key)))
end
elseif type(key) == 'table' then
if map_config.method == 'body' then
@@ -150,7 +138,7 @@ local function query_external_map(map_config, upstreams, key, callback, task)
local params_table = {}
for k, v in pairs(key) do
if type(v) == 'string' then
- table.insert(params_table, string.format('%s=%s', url_encode_string(k), url_encode_string(v)))
+ table.insert(params_table, string.format('%s=%s', lua_util.url_encode_string(k), lua_util.url_encode_string(v)))
end
end
url = string.format('%s?%s', url, table.concat(params_table, '&'))
diff --git a/lualib/lua_redis.lua b/lualib/lua_redis.lua
index 818d955e9..2c77c100a 100644
--- a/lualib/lua_redis.lua
+++ b/lualib/lua_redis.lua
@@ -1162,8 +1162,11 @@ local function prepare_redis_call(script)
end
-- Call load script on each server, set loaded flag
- script.in_flight = #servers
- for _, s in ipairs(servers) do
+ if not script.servers_ready then
+ script.servers_ready = { } -- possible values for each server: unsent, tempfail, failed, done
+ end
+
+ for idx, s in ipairs(servers) do
local cur_opts = {
host = s:get_addr(),
timeout = script.redis_params['timeout'],
@@ -1172,6 +1175,11 @@ local function prepare_redis_call(script)
upstream = s
}
+ -- By default we start from unsent status
+ if not script.servers_ready[idx] then
+ script.servers_ready[idx] = "unsent"
+ end
+
if script.redis_params['username'] then
cur_opts['username'] = script.redis_params['username']
end
@@ -1190,46 +1198,89 @@ local function prepare_redis_call(script)
return options
end
+local function is_all_servers_ready(script)
+ for _, s in ipairs(script.servers_ready) do
+ if s == "unsent" or s == "tempfail" then
+ return false
+ end
+ end
+
+ -- We assume that permanent errors are not recoverable, so we will just skip those servers
+ return true
+end
+
+local function is_all_servers_failed(script)
+ for _, s in ipairs(script.servers_ready) do
+ if s == "unsent" or s == "tempfail" or s == "done" then
+ return false
+ end
+ end
+
+ return true
+end
+
+local function script_description(script)
+ return script.filename and ("from file: " .. script.filename)
+ or ("with id: " .. script.id)
+end
+
local function load_script_task(script, task, is_write)
local rspamd_redis = require "rspamd_redis"
local opts = prepare_redis_call(script)
- for _, opt in ipairs(opts) do
- opt.task = task
- opt.is_write = is_write
- opt.callback = function(err, data)
- if err then
- logger.errx(task, 'cannot upload script to %s: %s; registered from: %s:%s',
- opt.upstream:get_addr():to_string(true),
- err, script.caller.short_src, script.caller.currentline)
- opt.upstream:fail()
- script.fatal_error = err
- else
- opt.upstream:ok()
- logger.infox(task,
- "uploaded redis script to %s %s %s, sha: %s",
- opt.upstream:get_addr():to_string(true),
- script.filename and "from file" or "with id", script.filename or script.id, data)
- script.sha = data -- We assume that sha is the same on all servers
- end
- script.in_flight = script.in_flight - 1
-
- if script.in_flight == 0 then
- script_set_loaded(script)
- end
- end
+ for idx, opt in ipairs(opts) do
+ if script.servers_ready[idx] ~= 'done' then
+ opt.task = task
+ opt.is_write = is_write
+ opt.callback = function(err, data)
+ if err then
+ if string.match(err, 'ERR') then
+ logger.errx(task, 'cannot upload script %s to %s: %s; registered from: %s:%s',
+ script_description(script),
+ opt.upstream:get_addr():to_string(true),
+ err, script.caller.short_src, script.caller.currentline)
+ opt.upstream:fail()
+ script.servers_ready[idx] = "failed"
+ return
+ else
+ -- Assume temporary error
+ logger.infox(task, 'temporary error uploading script %s to %s: %s; registered from: %s:%s',
+ script_description(script),
+ opt.upstream:get_addr():to_string(true),
+ err, script.caller.short_src, script.caller.currentline)
+ script.servers_ready[idx] = "tempfail"
+ return
+ end
+ else
+ opt.upstream:ok()
+ logger.infox(task,
+ "uploaded redis script to %s %s, sha: %s",
+ opt.upstream:get_addr():to_string(true),
+ script_description(script), data)
+ script.sha = data -- We assume that sha is the same on all servers
+ script.servers_ready[idx] = "done"
+ end
+ if is_all_servers_ready(script) then
+ script_set_loaded(script)
+ elseif is_all_servers_failed(script) then
+ script.fatal_error = "cannot upload script to any server"
+ end
+ end -- callback
- local ret = rspamd_redis.make_request(opt)
+ local ret = rspamd_redis.make_request(opt)
- if not ret then
- logger.errx('cannot execute redis request to load script on %s',
- opt.upstream:get_addr())
- script.in_flight = script.in_flight - 1
- opt.upstream:fail()
+ if not ret then
+ logger.errx('cannot execute redis request to load script on %s',
+ opt.upstream:get_addr())
+ script.servers_ready[idx] = "failed"
+ opt.upstream:fail()
+ end
end
- if script.in_flight == 0 then
+ if is_all_servers_ready(script) then
script_set_loaded(script)
+ elseif is_all_servers_failed(script) then
+ script.fatal_error = "cannot upload script to any server"
end
end
end
@@ -1238,44 +1289,62 @@ local function load_script_taskless(script, cfg, ev_base, is_write)
local rspamd_redis = require "rspamd_redis"
local opts = prepare_redis_call(script)
- for _, opt in ipairs(opts) do
- opt.config = cfg
- opt.ev_base = ev_base
- opt.is_write = is_write
- opt.callback = function(err, data)
- if err then
- logger.errx(cfg, 'cannot upload script to %s: %s; registered from: %s:%s, filename: %s',
- opt.upstream:get_addr():to_string(true),
- err, script.caller.short_src, script.caller.currentline, script.filename)
- opt.upstream:fail()
- script.fatal_error = err
- else
- opt.upstream:ok()
- logger.infox(cfg,
- "uploaded redis script to %s %s %s, sha: %s",
- opt.upstream:get_addr():to_string(true),
- script.filename and "from file" or "with id", script.filename or script.id,
- data)
- script.sha = data -- We assume that sha is the same on all servers
- script.fatal_error = nil
- end
- script.in_flight = script.in_flight - 1
+ for idx, opt in ipairs(opts) do
+ if script.servers_ready[idx] ~= 'done' then
+ opt.config = cfg
+ opt.ev_base = ev_base
+ opt.is_write = is_write
+ opt.callback = function(err, data)
+ if err then
+ if string.match(err, 'ERR') then
+ logger.errx(cfg, 'cannot upload script %s to %s: %s; registered from: %s:%s',
+ script_description(script),
+ opt.upstream:get_addr():to_string(true),
+ err, script.caller.short_src, script.caller.currentline)
+ opt.upstream:fail()
+ script.servers_ready[idx] = "failed"
+ return
+ else
+ -- Assume temporary error
+ logger.infox(cfg, 'temporary error uploading script %s to %s: %s; registered from: %s:%s',
+ script_description(script),
+ opt.upstream:get_addr():to_string(true),
+ err, script.caller.short_src, script.caller.currentline)
+ script.servers_ready[idx] = "tempfail"
+ return
+ end
+ else
+ opt.upstream:ok()
+ logger.infox(cfg,
+ "uploaded redis script %s to %s, sha: %s",
+ script_description(script),
+ opt.upstream:get_addr():to_string(true),
+ data)
+ script.sha = data -- We assume that sha is the same on all servers
+ script.servers_ready[idx] = "done"
+ end
- if script.in_flight == 0 then
- script_set_loaded(script)
+ if is_all_servers_ready(script) then
+ script_set_loaded(script)
+ elseif is_all_servers_failed(script) then
+ script.fatal_error = "cannot upload script to any server"
+ end
end
- end
- local ret = rspamd_redis.make_request(opt)
+ local ret = rspamd_redis.make_request(opt)
- if not ret then
- logger.errx('cannot execute redis request to load script on %s',
- opt.upstream:get_addr())
- script.in_flight = script.in_flight - 1
- opt.upstream:fail()
+ if not ret then
+ logger.errx('cannot execute redis request to load script %s on %s',
+ script_description(script),
+ opt.upstream:get_addr())
+ script.servers_ready[idx] = "failed"
+ opt.upstream:fail()
+ end
end
- if script.in_flight == 0 then
+ if is_all_servers_ready(script) then
script_set_loaded(script)
+ elseif is_all_servers_failed(script) then
+ script.fatal_error = "cannot upload script " .. script_description(script) .. " to any server"
end
end
end
@@ -1306,12 +1375,17 @@ local function add_redis_script(script, redis_params, caller_level, maybe_filena
rspamd_config:add_on_load(function(cfg, ev_base, worker)
local mult = 0.0
rspamd_config:add_periodic(ev_base, 0.0, function()
- if not new_script.sha then
+ if not new_script.sha and not new_script.fatal_error then
load_redis_script(new_script, cfg, ev_base, worker)
- mult = mult + 1
+
+ -- Do not wait for more than 10 seconds to retry
+ if mult < 10 then
+ mult = mult + 1
+ end
return 1.0 * mult -- Check one more time in one second
end
+ -- All loaded, we are good to go
return false
end, false)
end)
@@ -1386,10 +1460,12 @@ local function exec_redis_script(id, params, callback, keys, args)
callback(err, data)
elseif string.match(err, 'NOSCRIPT') then
-- Schedule restart
+ logger.infox(params.task, 'redis script %s is not loaded (NOSCRIPT returned), scheduling reload',
+ script_description(script))
script.sha = nil
if can_reload then
table.insert(script.waitq, do_call)
- if script.in_flight == 0 then
+ if not script.servers_ready then
-- Reload scripts if this has not been initiated yet
if params.task then
load_script_task(script, params.task)
@@ -1437,6 +1513,8 @@ local function exec_redis_script(id, params, callback, keys, args)
do_call(true)
else
-- Delayed until scripts are loaded
+ logger.infox(params.task or rspamd_config, 'redis script %s is not loaded, trying to load',
+ script_description(script))
if not params.task then
table.insert(script.waitq, do_call)
else
diff --git a/lualib/lua_scanners/icap.lua b/lualib/lua_scanners/icap.lua
index 682562d85..2e3ced034 100644
--- a/lualib/lua_scanners/icap.lua
+++ b/lualib/lua_scanners/icap.lua
@@ -245,7 +245,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
local req_hlen = 2
if maybe_part then
table.insert(req_headers,
- string.format('GET http://%s/%s HTTP/1.0\r\n', in_client_ip, maybe_part:get_filename()))
+ string.format('GET http://%s/%s HTTP/1.0\r\n', in_client_ip, lua_util.url_encode_string(maybe_part:get_filename())))
if rule.use_specific_content_type then
table.insert(http_headers, string.format('Content-Type: %s/%s\r\n', maybe_part:get_detected_type()))
--else
diff --git a/lualib/lua_util.lua b/lualib/lua_util.lua
index 650ad5db1..470925b95 100644
--- a/lualib/lua_util.lua
+++ b/lualib/lua_util.lua
@@ -755,9 +755,29 @@ local function override_defaults(def, override)
local res = {}
- for k, v in pairs(override) do
- if type(v) == 'table' then
- if def[k] and type(def[k]) == 'table' then
+ -- Allow transparent ucl
+ local pairs_func, def_pairs_func = pairs, pairs
+ local type_func, def_type_func = type, type
+ if type(override[0]) == 'userdata' then
+ pairs_func = function(t)
+ return t:pairs()
+ end
+ type_func = function(t)
+ return t:type()
+ end
+ end
+ if type(def[0]) == 'userdata' then
+ def_pairs_func = function(t)
+ return t:pairs()
+ end
+ def_type_func = function(t)
+ return t:type()
+ end
+ end
+
+ for k, v in pairs_func(override) do
+ if type_func(v) == 'table' then
+ if def[k] and def_type_func(def[k]) == 'table' then
-- Recursively override elements
res[k] = override_defaults(def[k], v)
else
@@ -768,7 +788,7 @@ local function override_defaults(def, override)
end
end
- for k, v in pairs(def) do
+ for k, v in def_pairs_func(def) do
if type(res[k]) == 'nil' then
res[k] = v
end
@@ -1667,6 +1687,30 @@ local function join_path(...)
end
exports.join_path = join_path
+---[[[
+-- @function lua_util.url_encode_string(str)
+-- URL encodes a string
+--
+-- @param {string} str string to encode
+-- @return {string} URL encoded string
+--
+---]]]
+local function url_encode_string(str)
+ if str == nil then
+ return ''
+ end
+ str = string.gsub(str, "([^%w _%%%-%.~])",
+ function(c)
+ return string.format("%%%02X", string.byte(c))
+ end)
+ str = string.gsub(str, " ", "+")
+ return str
+end
+exports.url_encode_string = url_encode_string
+
+assert(url_encode_string('上海+中國') == '%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B')
+assert(url_encode_string('? and the Mysterians') == '%3F+and+the+Mysterians')
+
-- Short unit test for sanity
if path_sep == '/' then
assert(join_path('/path', 'to', 'file') == '/path/to/file')
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..67504a812
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2554 @@
+{
+ "name": "rspamd",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@stylistic/eslint-plugin": "*",
+ "eslint": "^9.7.0",
+ "postcss-html": "*",
+ "stylelint": ">=13.6.0",
+ "stylelint-config-standard": "*"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
+ "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.24.7",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
+ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz",
+ "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.24.7",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz",
+ "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^2.4.1"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz",
+ "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ }
+ },
+ "node_modules/@csstools/media-query-list-parser": {
+ "version": "2.1.13",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz",
+ "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^2.7.1",
+ "@csstools/css-tokenizer": "^2.4.1"
+ }
+ },
+ "node_modules/@csstools/selector-specificity": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz",
+ "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^6.0.13"
+ }
+ },
+ "node_modules/@dual-bundle/import-meta-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+ "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz",
+ "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz",
+ "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.3.0.tgz",
+ "integrity": "sha512-rtiz6u5gRyyEZp36FcF1/gHJbsbT3qAgXZ1qkad6Nr/xJ9wrSJkiSFFQhpYVTIZ7FJNRJurEcumZDCwN9dEI4g==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.3.0",
+ "@stylistic/eslint-plugin-jsx": "2.3.0",
+ "@stylistic/eslint-plugin-plus": "2.3.0",
+ "@stylistic/eslint-plugin-ts": "2.3.0",
+ "@types/eslint": "^8.56.10"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.3.0.tgz",
+ "integrity": "sha512-lQwoiYb0Fs6Yc5QS3uT8+T9CPKK2Eoxc3H8EnYJgM26v/DgtW+1lvy2WNgyBflU+ThShZaHm3a6CdD9QeKx23w==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "^8.56.10",
+ "acorn": "^8.11.3",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-jsx": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.3.0.tgz",
+ "integrity": "sha512-tsQ0IEKB195H6X9A4iUSgLLLKBc8gUBWkBIU8tp1/3g2l8stu+PtMQVV/VmK1+3bem5FJCyvfcZIQ/WF1fsizA==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "^2.3.0",
+ "@types/eslint": "^8.56.10",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.3.0.tgz",
+ "integrity": "sha512-xboPWGUU5yaPlR+WR57GwXEuY4PSlPqA0C3IdNA/+1o2MuBi95XgDJcZiJ9N+aXsqBXAPIpFFb+WQ7QEHo4f7g==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "^8.56.10",
+ "@typescript-eslint/utils": "^7.12.0"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz",
+ "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "7.16.1",
+ "@typescript-eslint/types": "7.16.1",
+ "@typescript-eslint/typescript-estree": "7.16.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.3.0.tgz",
+ "integrity": "sha512-wqOR38/uz/0XPnHX68ftp8sNMSAqnYGjovOTN7w00xnjS6Lxr3Sk7q6AaxWWqbMvOj7V2fQiMC5HWAbTruJsCg==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.3.0",
+ "@types/eslint": "^8.56.10",
+ "@typescript-eslint/utils": "^7.12.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz",
+ "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "7.16.1",
+ "@typescript-eslint/types": "7.16.1",
+ "@typescript-eslint/typescript-estree": "7.16.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.56.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+ "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz",
+ "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.16.1",
+ "@typescript-eslint/visitor-keys": "7.16.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz",
+ "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz",
+ "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.16.1",
+ "@typescript-eslint/visitor-keys": "7.16.1",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.16.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz",
+ "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.16.1",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-functions-list": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz",
+ "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12 || >=16"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+ "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+ "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz",
+ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.17.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.7.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.0.2",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
+ "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.12.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz",
+ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==",
+ "dev": true
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/global-modules": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "dependencies": {
+ "global-prefix": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "dependencies": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/global-prefix/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globjoin": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
+ "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/html-tags": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz",
+ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
+ "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/known-css-properties": {
+ "version": "0.34.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz",
+ "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==",
+ "dev": true
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
+ "dev": true
+ },
+ "node_modules/mathml-tag-names": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
+ "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.30",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+ "dev": true
+ },
+ "node_modules/meow": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz",
+ "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+ "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.39",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
+ "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.1",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-html": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.7.0.tgz",
+ "integrity": "sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==",
+ "dev": true,
+ "dependencies": {
+ "htmlparser2": "^8.0.0",
+ "js-tokens": "^9.0.0",
+ "postcss": "^8.4.0",
+ "postcss-safe-parser": "^6.0.0"
+ },
+ "engines": {
+ "node": "^12 || >=14"
+ }
+ },
+ "node_modules/postcss-resolve-nested-selector": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz",
+ "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==",
+ "dev": true
+ },
+ "node_modules/postcss-safe-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
+ "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3.3"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
+ "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint": {
+ "version": "16.7.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.7.0.tgz",
+ "integrity": "sha512-Q1ATiXlz+wYr37a7TGsfvqYn2nSR3T/isw3IWlZQzFzCNoACHuGBb6xBplZXz56/uDRJHIygxjh7jbV/8isewA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^2.7.1",
+ "@csstools/css-tokenizer": "^2.4.1",
+ "@csstools/media-query-list-parser": "^2.1.13",
+ "@csstools/selector-specificity": "^3.1.1",
+ "@dual-bundle/import-meta-resolve": "^4.1.0",
+ "balanced-match": "^2.0.0",
+ "colord": "^2.9.3",
+ "cosmiconfig": "^9.0.0",
+ "css-functions-list": "^3.2.2",
+ "css-tree": "^2.3.1",
+ "debug": "^4.3.5",
+ "fast-glob": "^3.3.2",
+ "fastest-levenshtein": "^1.0.16",
+ "file-entry-cache": "^9.0.0",
+ "global-modules": "^2.0.0",
+ "globby": "^11.1.0",
+ "globjoin": "^0.1.4",
+ "html-tags": "^3.3.1",
+ "ignore": "^5.3.1",
+ "imurmurhash": "^0.1.4",
+ "is-plain-object": "^5.0.0",
+ "known-css-properties": "^0.34.0",
+ "mathml-tag-names": "^2.1.3",
+ "meow": "^13.2.0",
+ "micromatch": "^4.0.7",
+ "normalize-path": "^3.0.0",
+ "picocolors": "^1.0.1",
+ "postcss": "^8.4.39",
+ "postcss-resolve-nested-selector": "^0.1.1",
+ "postcss-safe-parser": "^7.0.0",
+ "postcss-selector-parser": "^6.1.0",
+ "postcss-value-parser": "^4.2.0",
+ "resolve-from": "^5.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^7.1.0",
+ "supports-hyperlinks": "^3.0.0",
+ "svg-tags": "^1.0.0",
+ "table": "^6.8.2",
+ "write-file-atomic": "^5.0.1"
+ },
+ "bin": {
+ "stylelint": "bin/stylelint.mjs"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
+ "node_modules/stylelint-config-recommended": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz",
+ "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "stylelint": "^16.1.0"
+ }
+ },
+ "node_modules/stylelint-config-standard": {
+ "version": "36.0.1",
+ "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz",
+ "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "dependencies": {
+ "stylelint-config-recommended": "^14.0.1"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "stylelint": "^16.1.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/stylelint/node_modules/balanced-match": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
+ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
+ "dev": true
+ },
+ "node_modules/stylelint/node_modules/file-entry-cache": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz",
+ "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/stylelint/node_modules/flat-cache": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz",
+ "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.3.1",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/stylelint/node_modules/postcss-safe-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz",
+ "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "engines": {
+ "node": ">=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/stylelint/node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/stylelint/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz",
+ "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/svg-tags": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
+ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+ "dev": true
+ },
+ "node_modules/table": {
+ "version": "6.8.2",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz",
+ "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/table/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/table/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
+ "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/write-file-atomic": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
+ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index dd1bca360..58fc7ba62 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,9 @@
{
"devDependencies": {
- "eslint": "*",
"@stylistic/eslint-plugin": "*",
+ "eslint": "^9.7.0",
+ "postcss-html": "*",
"stylelint": ">=13.6.0",
- "stylelint-config-standard": "*",
- "postcss-html": "*"
- },
- "eslintIgnore": [
- "*.min.js",
- "index.html",
- "prism.js"
- ]
+ "stylelint-config-standard": "*"
+ }
}
diff --git a/rules/archives.lua b/rules/archives.lua
index 83ac27df8..c582b93bd 100644
--- a/rules/archives.lua
+++ b/rules/archives.lua
@@ -2,19 +2,19 @@ local rspamd_regexp = require "rspamd_regexp"
local lua_maps = require "lua_maps"
local clickbait_map = lua_maps.map_add_from_ucl(
- {
- string.format('%s/maps.d/%s', rspamd_paths.CONFDIR, 'exe_clickbait.inc'),
- string.format('%s/local.d/maps.d/%s', rspamd_paths.LOCAL_CONFDIR, 'exe_clickbait.inc')
- },
- 'regexp',
- 'Inappropriate descriptions for executables'
+ {
+ string.format('%s/maps.d/%s', rspamd_paths.CONFDIR, 'exe_clickbait.inc'),
+ string.format('%s/local.d/maps.d/%s', rspamd_paths.LOCAL_CONFDIR, 'exe_clickbait.inc')
+ },
+ 'regexp',
+ 'Inappropriate descriptions for executables'
)
local exe_re = rspamd_regexp.create_cached([[/\.exe$|\.com$/i]])
local img_re = rspamd_regexp.create_cached([[/\.img$/i]])
local rar_re = rspamd_regexp.create_cached([[/\.rar$|\.r[0-9]{2}$/i]])
-local id = rspamd_config:register_symbol{
+local id = rspamd_config:register_symbol {
callback = function(task)
local num_checked = 0
local have_subject_clickbait = false
@@ -52,7 +52,7 @@ local id = rspamd_config:register_symbol{
local name = info.name
if img_re:match(name) then
- local ratio = info.uncompressed_size/info.compressed_size
+ local ratio = info.uncompressed_size / info.compressed_size
if ratio >= 500 then
task:insert_result('UDF_COMPRESSION_500PLUS', 1.0)
end
@@ -86,7 +86,7 @@ local id = rspamd_config:register_symbol{
type = 'callback',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'exe file in archive with clickbait filename',
group = 'malware',
name = 'EXE_ARCHIVE_CLICKBAIT_FILENAME',
@@ -96,7 +96,7 @@ rspamd_config:register_symbol{
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'exe file in archive with clickbait subject',
group = 'malware',
name = 'EXE_ARCHIVE_CLICKBAIT_SUBJECT',
@@ -106,47 +106,47 @@ rspamd_config:register_symbol{
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'exe file in archive',
group = 'malware',
name = 'EXE_IN_ARCHIVE',
one_shot = true,
parent = id,
- score = 0.5,
+ score = 1.5,
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'rar with wrong extension containing exe file',
group = 'malware',
name = 'EXE_IN_MISIDENTIFIED_RAR',
one_shot = true,
parent = id,
- score = 2.0,
+ score = 5.0,
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'rar with wrong extension',
group = 'malware',
name = 'MISIDENTIFIED_RAR',
one_shot = true,
parent = id,
- score = 2.0,
+ score = 4.0,
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'single file container bearing executable',
group = 'malware',
name = 'SINGLE_FILE_ARCHIVE_WITH_EXE',
one_shot = true,
parent = id,
- score = 1.0,
+ score = 5.0,
type = 'virtual',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
description = 'very well compressed img file in archive',
name = 'UDF_COMPRESSION_500PLUS',
one_shot = true,
diff --git a/rules/misc.lua b/rules/misc.lua
index faf4a8fb8..4ddb00dfb 100644
--- a/rules/misc.lua
+++ b/rules/misc.lua
@@ -862,3 +862,71 @@ rspamd_config.COMPLETELY_EMPTY = {
group = 'blankspam',
score = 15
}
+
+-- Preserve compatibility
+local rdns_auth_and_local_conf = lua_util.config_check_local_or_authed(rspamd_config, 'once_received',
+ false, false)
+-- Check for the hostname if it was not set
+local rnds_check_id = rspamd_config:register_symbol {
+ name = 'RDNS_CHECK',
+ callback = function(task)
+ if not task:get_hostname() then
+ -- Try to resolve
+ local task_ip = task:get_ip()
+ if task_ip and task_ip:is_valid() then
+ local rspamd_logger = require "rspamd_logger"
+ local function rdns_dns_cb(_, to_resolve, results, err)
+ if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then
+ rspamd_logger.errx(task, 'error looking up %s: %s', to_resolve, err)
+ task:insert_result('RDNS_DNSFAIL', 1.0)
+ end
+
+ if not results then
+ task:insert_result('RDNS_NONE', 1.0)
+ else
+ rspamd_logger.infox(task, 'source hostname has not been passed to Rspamd from MTA, ' ..
+ 'but we could resolve source IP address PTR %s as "%s"',
+ to_resolve, results[1])
+ task:set_hostname(results[1])
+ end
+ end
+ task:get_resolver():resolve_ptr({ task = task,
+ name = task_ip:to_string(),
+ callback = rdns_dns_cb,
+ forced = true
+ })
+ end
+ end
+ end,
+ type = 'prefilter',
+ -- TODO: settings might need to use this symbol if they depend on hostname...
+ priority = lua_util.symbols_priorities.top - 1,
+ description = 'Check if hostname has been resolved by MTA',
+ condition = function(task)
+ local task_ip = task:get_ip()
+ if ((not rdns_auth_and_local_conf[1] and task:get_user()) or
+ (not rdns_auth_and_local_conf[2] and task_ip and task_ip:is_local())) then
+ return false
+ end
+
+ return true
+ end
+}
+
+rspamd_config:register_symbol {
+ type = 'virtual',
+ name = 'RDNS_DNSFAIL',
+ score = 0.0,
+ description = 'DNS failure resolving RDNS',
+ group = 'hfilter',
+ parent = rnds_check_id,
+
+}
+rspamd_config:register_symbol {
+ type = 'virtual',
+ name = 'RDNS_NONE',
+ score = 2.0,
+ description = 'DNS failure resolving RDNS',
+ group = 'hfilter',
+ parent = rnds_check_id,
+} \ No newline at end of file
diff --git a/src/client/rspamc.cxx b/src/client/rspamc.cxx
index 1e0830493..1c67e4167 100644
--- a/src/client/rspamc.cxx
+++ b/src/client/rspamc.cxx
@@ -524,16 +524,13 @@ rspamc_password_callback(const char *option_name,
else {
/* Strip trailing spaces */
auto *map = (char *) locked_mmap.value().get_map();
- auto *end = map + locked_mmap.value().get_size() - 1;
-
- while (g_ascii_isspace(*end) && end > map) {
- end--;
- }
-
- end++;
- value_view = std::string_view{map, static_cast<std::size_t>(end - map + 1)};
- processed_passwd.assign(std::begin(value_view), std::end(value_view));
- processed_passwd.push_back('\0');
+ value_view = std::string_view{map, locked_mmap->get_size()};
+ auto right = value_view.end() - 1;
+ for (; right > value_view.cbegin() && g_ascii_isspace(*right); --right)
+ ;
+ std::string_view str{value_view.begin(), static_cast<size_t>(right - value_view.begin()) + 1};
+ processed_passwd.assign(std::begin(str), std::end(str));
+ processed_passwd.push_back('\0'); /* Null-terminate for C part */
}
}
else {
diff --git a/src/client/rspamdclient.c b/src/client/rspamdclient.c
index 2b8d0e9bb..bcb3cf67c 100644
--- a/src/client/rspamdclient.c
+++ b/src/client/rspamdclient.c
@@ -302,12 +302,10 @@ rspamd_client_init(struct rspamd_http_context *http_ctx,
conn->timeout = timeout;
if (key) {
- conn->key = rspamd_pubkey_from_base32(key, 0, RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ conn->key = rspamd_pubkey_from_base32(key, 0, RSPAMD_KEYPAIR_KEX);
if (conn->key) {
- conn->keypair = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ conn->keypair = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
rspamd_http_connection_set_key(conn->http_conn, conn->keypair);
}
else {
diff --git a/src/controller.c b/src/controller.c
index d91f99098..386448f93 100644
--- a/src/controller.c
+++ b/src/controller.c
@@ -1945,7 +1945,7 @@ rspamd_controller_learn_fin_task(void *ud)
if (task->err != NULL) {
msg_info_session("cannot learn <%s>: %e",
- MESSAGE_FIELD(task, message_id), task->err);
+ MESSAGE_FIELD_CHECK(task, message_id), task->err);
rspamd_controller_send_error(conn_ent, task->err->code, "%s",
task->err->message);
@@ -1957,14 +1957,14 @@ rspamd_controller_learn_fin_task(void *ud)
msg_info_task("<%s> learned message as %s: %s",
rspamd_inet_address_to_string(session->from_addr),
session->is_spam ? "spam" : "ham",
- MESSAGE_FIELD(task, message_id));
+ MESSAGE_FIELD_CHECK(task, message_id));
rspamd_controller_send_string(conn_ent, "{\"success\":true}");
return TRUE;
}
if (!rspamd_task_process(task, RSPAMD_TASK_PROCESS_LEARN)) {
msg_info_task("cannot learn <%s>: %e",
- MESSAGE_FIELD(task, message_id), task->err);
+ MESSAGE_FIELD_CHECK(task, message_id), task->err);
if (task->err) {
rspamd_controller_send_error(conn_ent, task->err->code, "%s",
@@ -1985,7 +1985,7 @@ rspamd_controller_learn_fin_task(void *ud)
msg_info_task("<%s> learned message as %s: %s",
rspamd_inet_address_to_string(session->from_addr),
session->is_spam ? "spam" : "ham",
- MESSAGE_FIELD(task, message_id));
+ MESSAGE_FIELD_CHECK(task, message_id));
rspamd_controller_send_string(conn_ent, "{\"success\":true}");
}
@@ -2114,7 +2114,7 @@ rspamd_controller_handle_learn_common(
if (!rspamd_task_process(task, RSPAMD_TASK_PROCESS_LEARN)) {
msg_warn_session("<%s> message cannot be processed",
- MESSAGE_FIELD(task, message_id));
+ MESSAGE_FIELD_CHECK(task, message_id));
goto end;
}
diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c
index 445289511..5fd3303dc 100644
--- a/src/fuzzy_storage.c
+++ b/src/fuzzy_storage.c
@@ -1088,8 +1088,7 @@ rspamd_fuzzy_make_reply(struct rspamd_fuzzy_cmd *cmd,
len,
session->reply.hdr.nonce,
session->nm,
- session->reply.hdr.mac,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ session->reply.hdr.mac);
}
else if (default_disabled) {
/* Hash is from a forbidden flag by default, and there is no encryption override */
@@ -1668,8 +1667,7 @@ rspamd_fuzzy_decrypt_command(struct fuzzy_session *s, unsigned char *buf, gsize
}
/* Now process the remote pubkey */
- rk = rspamd_pubkey_from_bin(hdr.pubkey, sizeof(hdr.pubkey),
- RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
+ rk = rspamd_pubkey_from_bin(hdr.pubkey, sizeof(hdr.pubkey), RSPAMD_KEYPAIR_KEX);
if (rk == NULL) {
msg_err("bad key; ip=%s",
@@ -1683,7 +1681,7 @@ rspamd_fuzzy_decrypt_command(struct fuzzy_session *s, unsigned char *buf, gsize
/* Now decrypt request */
if (!rspamd_cryptobox_decrypt_nm_inplace(buf, buflen, hdr.nonce,
rspamd_pubkey_get_nm(rk, key->key),
- hdr.mac, RSPAMD_CRYPTOBOX_MODE_25519)) {
+ hdr.mac)) {
msg_err("decryption failed; ip=%s",
rspamd_inet_address_to_string(s->addr));
rspamd_pubkey_unref(rk);
@@ -2771,8 +2769,7 @@ fuzzy_add_keypair_from_ucl(const ucl_object_t *obj, khash_t(rspamd_fuzzy_keys_ha
return NULL;
}
- if (rspamd_keypair_alg(kp) != RSPAMD_CRYPTOBOX_MODE_25519 ||
- rspamd_keypair_type(kp) != RSPAMD_KEYPAIR_KEX) {
+ if (rspamd_keypair_type(kp) != RSPAMD_KEYPAIR_KEX) {
return FALSE;
}
@@ -2837,7 +2834,7 @@ fuzzy_add_keypair_from_ucl(const ucl_object_t *obj, khash_t(rspamd_fuzzy_keys_ha
}
}
- msg_debug("loaded keypair %*bs", rspamd_cryptobox_pk_bytes(RSPAMD_CRYPTOBOX_MODE_25519), pk);
+ msg_debug("loaded keypair %*bs", crypto_box_publickeybytes(), pk);
return key;
}
diff --git a/src/libcryptobox/cryptobox.c b/src/libcryptobox/cryptobox.c
index aa093d01e..eeeed020c 100644
--- a/src/libcryptobox/cryptobox.c
+++ b/src/libcryptobox/cryptobox.c
@@ -38,19 +38,8 @@
#endif
#ifdef HAVE_OPENSSL
#include <openssl/opensslv.h>
-/* Openssl >= 1.0.1d is required for GCM verification */
-#if OPENSSL_VERSION_NUMBER >= 0x1000104fL
-#define HAVE_USABLE_OPENSSL 1
-#endif
-#endif
-
-#ifdef HAVE_USABLE_OPENSSL
#include <openssl/evp.h>
-#include <openssl/ec.h>
-#include <openssl/ecdh.h>
-#include <openssl/ecdsa.h>
-#include <openssl/rand.h>
-#define CRYPTOBOX_CURVE_NID NID_X9_62_prime256v1
+#include <openssl/rsa.h>
#endif
#include <signal.h>
@@ -324,759 +313,327 @@ void rspamd_cryptobox_deinit(struct rspamd_cryptobox_library_ctx *ctx)
}
}
-void rspamd_cryptobox_keypair(rspamd_pk_t pk, rspamd_sk_t sk,
- enum rspamd_cryptobox_mode mode)
+void rspamd_cryptobox_keypair(rspamd_pk_t pk, rspamd_sk_t sk)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- ottery_rand_bytes(sk, rspamd_cryptobox_MAX_SKBYTES);
- sk[0] &= 248;
- sk[31] &= 127;
- sk[31] |= 64;
-
- crypto_scalarmult_base(pk, sk);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EC_KEY *ec_sec;
- const BIGNUM *bn_sec;
-
- const EC_POINT *ec_pub;
- gsize len;
-
- ec_sec = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- g_assert(ec_sec != NULL);
- g_assert(EC_KEY_generate_key(ec_sec) != 0);
-
- bn_sec = EC_KEY_get0_private_key(ec_sec);
- g_assert(bn_sec != NULL);
- ec_pub = EC_KEY_get0_public_key(ec_sec);
- g_assert(ec_pub != NULL);
-#if OPENSSL_VERSION_MAJOR >= 3
- unsigned char *buf = NULL; /* Thanks openssl for this API (no) */
- len = EC_POINT_point2buf(EC_KEY_get0_group(ec_sec), ec_pub,
- POINT_CONVERSION_UNCOMPRESSED, &buf, NULL);
- g_assert(len <= (int) rspamd_cryptobox_pk_bytes(mode));
- memcpy(pk, buf, len);
- OPENSSL_free(buf);
-#else
- BIGNUM *bn_pub;
- bn_pub = EC_POINT_point2bn(EC_KEY_get0_group(ec_sec),
- ec_pub, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL);
- len = BN_num_bytes(bn_pub);
- g_assert(len <= (int) rspamd_cryptobox_pk_bytes(mode));
- BN_bn2bin(bn_pub, pk);
- BN_free(bn_pub);
-#endif
+ ottery_rand_bytes(sk, rspamd_cryptobox_MAX_SKBYTES);
+ sk[0] &= 248;
+ sk[31] &= 127;
+ sk[31] |= 64;
- len = BN_num_bytes(bn_sec);
- g_assert(len <= (int) sizeof(rspamd_sk_t));
- BN_bn2bin(bn_sec, sk);
+ crypto_scalarmult_base(pk, sk);
+}
- EC_KEY_free(ec_sec);
-#endif
- }
+void rspamd_cryptobox_keypair_sig(rspamd_sig_pk_t pk, rspamd_sig_sk_t sk)
+{
+ crypto_sign_keypair(pk, sk);
}
-void rspamd_cryptobox_keypair_sig(rspamd_sig_pk_t pk, rspamd_sig_sk_t sk,
- enum rspamd_cryptobox_mode mode)
+void rspamd_cryptobox_nm(rspamd_nm_t nm,
+ const rspamd_pk_t pk, const rspamd_sk_t sk)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_sign_keypair(pk, sk);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EC_KEY *ec_sec;
- const BIGNUM *bn_sec;
- const EC_POINT *ec_pub;
- gsize len;
-
- ec_sec = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- g_assert(ec_sec != NULL);
- g_assert(EC_KEY_generate_key(ec_sec) != 0);
-
- bn_sec = EC_KEY_get0_private_key(ec_sec);
- g_assert(bn_sec != NULL);
- ec_pub = EC_KEY_get0_public_key(ec_sec);
- g_assert(ec_pub != NULL);
-
-#if OPENSSL_VERSION_MAJOR >= 3
- unsigned char *buf = NULL; /* Thanks openssl for this API (no) */
- len = EC_POINT_point2buf(EC_KEY_get0_group(ec_sec), ec_pub,
- POINT_CONVERSION_UNCOMPRESSED, &buf, NULL);
- g_assert(len <= (int) rspamd_cryptobox_pk_bytes(mode));
- memcpy(pk, buf, len);
- OPENSSL_free(buf);
-#else
- BIGNUM *bn_pub;
- bn_pub = EC_POINT_point2bn(EC_KEY_get0_group(ec_sec),
- ec_pub, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL);
- len = BN_num_bytes(bn_pub);
- g_assert(len <= (int) rspamd_cryptobox_pk_bytes(mode));
- BN_bn2bin(bn_pub, pk);
- BN_free(bn_pub);
-#endif
+ unsigned char s[32];
+ unsigned char e[32];
- len = BN_num_bytes(bn_sec);
- g_assert(len <= (int) sizeof(rspamd_sk_t));
- BN_bn2bin(bn_sec, sk);
- EC_KEY_free(ec_sec);
-#endif
+ memcpy(e, sk, 32);
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ if (crypto_scalarmult(s, e, pk) != -1) {
+ hchacha(s, n0, nm, 20);
}
+
+ rspamd_explicit_memzero(e, 32);
}
-#if OPENSSL_VERSION_MAJOR >= 3
-/* Compatibility function for OpenSSL 3.0 - thanks for breaking all API one more time */
-EC_POINT *ec_point_bn2point_compat(const EC_GROUP *group,
- const BIGNUM *bn, EC_POINT *point, BN_CTX *ctx)
+void rspamd_cryptobox_sign(unsigned char *sig, unsigned long long *siglen_p,
+ const unsigned char *m, gsize mlen,
+ const rspamd_sig_sk_t sk)
{
- size_t buf_len = 0;
- unsigned char *buf;
- EC_POINT *ret;
-
- if ((buf_len = BN_num_bytes(bn)) == 0)
- buf_len = 1;
- if ((buf = OPENSSL_malloc(buf_len)) == NULL) {
- return NULL;
- }
+ crypto_sign_detached(sig, siglen_p, m, mlen, sk);
+}
- if (!BN_bn2binpad(bn, buf, buf_len)) {
- OPENSSL_free(buf);
- return NULL;
- }
+#ifdef HAVE_OPENSSL
+bool rspamd_cryptobox_verify_evp_ed25519(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ struct evp_pkey_st *pub_key)
+{
+ bool ret = false;
- if (point == NULL) {
- if ((ret = EC_POINT_new(group)) == NULL) {
- OPENSSL_free(buf);
- return NULL;
- }
- }
- else
- ret = point;
-
- if (!EC_POINT_oct2point(group, ret, buf, buf_len, ctx)) {
- if (ret != point)
- EC_POINT_clear_free(ret);
- OPENSSL_free(buf);
- return NULL;
+ if (siglen == crypto_sign_bytes()) {
+ rspamd_pk_t pk;
+ size_t len_pk = sizeof(rspamd_pk_t);
+ EVP_PKEY_get_raw_public_key(pub_key, pk, &len_pk);
+ ret = (crypto_sign_verify_detached(sig, digest, dlen, pk) == 0);
}
- OPENSSL_free(buf);
return ret;
}
-#else
-#define ec_point_bn2point_compat EC_POINT_bn2point
-#endif
-void rspamd_cryptobox_nm(rspamd_nm_t nm,
- const rspamd_pk_t pk, const rspamd_sk_t sk,
- enum rspamd_cryptobox_mode mode)
+bool rspamd_cryptobox_verify_evp_ecdsa(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ EVP_PKEY *pub_key)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- unsigned char s[32];
- unsigned char e[32];
+ bool ret = false;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pub_key, NULL);
+ g_assert(pctx != NULL);
+ EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
+ const EVP_MD *md = EVP_get_digestbynid(nid);
- memcpy(e, sk, 32);
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
+ g_assert(EVP_PKEY_verify_init(pctx) == 1);
+ g_assert(EVP_PKEY_CTX_set_signature_md(pctx, md) == 1);
- if (crypto_scalarmult(s, e, pk) != -1) {
- hchacha(s, n0, nm, 20);
- }
+ ret = (EVP_PKEY_verify(pctx, sig, siglen, digest, dlen) == 1);
- rspamd_explicit_memzero(e, 32);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EC_KEY *lk;
- EC_POINT *ec_pub;
- BIGNUM *bn_pub, *bn_sec;
- int len;
- unsigned char s[32];
-
- lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- g_assert(lk != NULL);
-
- bn_pub = BN_bin2bn(pk, rspamd_cryptobox_pk_bytes(mode), NULL);
- g_assert(bn_pub != NULL);
- bn_sec = BN_bin2bn(sk, sizeof(rspamd_sk_t), NULL);
- g_assert(bn_sec != NULL);
-
- g_assert(EC_KEY_set_private_key(lk, bn_sec) == 1);
- ec_pub = ec_point_bn2point_compat(EC_KEY_get0_group(lk), bn_pub, NULL, NULL);
- g_assert(ec_pub != NULL);
- len = ECDH_compute_key(s, sizeof(s), ec_pub, lk, NULL);
- g_assert(len == sizeof(s));
-
- /* Still do hchacha iteration since we are not using SHA1 KDF */
- hchacha(s, n0, nm, 20);
+ EVP_PKEY_CTX_free(pctx);
+ EVP_MD_CTX_free(mdctx);
- EC_KEY_free(lk);
- EC_POINT_free(ec_pub);
- BN_free(bn_sec);
- BN_free(bn_pub);
-#endif
- }
+ return ret;
}
-
-void rspamd_cryptobox_sign(unsigned char *sig, unsigned long long *siglen_p,
- const unsigned char *m, gsize mlen,
- const rspamd_sk_t sk,
- enum rspamd_cryptobox_mode mode)
+bool rspamd_cryptobox_verify_evp_rsa(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ EVP_PKEY *pub_key)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_sign_detached(sig, siglen_p, m, mlen, sk);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EC_KEY *lk;
- BIGNUM *bn_sec;
- EVP_MD_CTX *sha_ctx;
- unsigned char h[64];
- unsigned int diglen = rspamd_cryptobox_signature_bytes(mode);
-
- /* Prehash */
- sha_ctx = EVP_MD_CTX_create();
- g_assert(EVP_DigestInit(sha_ctx, EVP_sha512()) == 1);
- EVP_DigestUpdate(sha_ctx, m, mlen);
- EVP_DigestFinal(sha_ctx, h, NULL);
-
- /* Key setup */
- lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- g_assert(lk != NULL);
- bn_sec = BN_bin2bn(sk, sizeof(rspamd_sk_t), NULL);
- g_assert(bn_sec != NULL);
- g_assert(EC_KEY_set_private_key(lk, bn_sec) == 1);
-
- /* ECDSA */
- g_assert(ECDSA_sign(0, h, sizeof(h), sig, &diglen, lk) == 1);
- g_assert(diglen <= sizeof(rspamd_signature_t));
-
- if (siglen_p) {
- *siglen_p = diglen;
- }
+ bool ret = false;
- EC_KEY_free(lk);
- EVP_MD_CTX_destroy(sha_ctx);
- BN_free(bn_sec);
-#endif
- }
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pub_key, NULL);
+ g_assert(pctx != NULL);
+ EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
+ const EVP_MD *md = EVP_get_digestbynid(nid);
+
+ g_assert(EVP_PKEY_verify_init(pctx) == 1);
+ g_assert(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) == 1);
+ g_assert(EVP_PKEY_CTX_set_signature_md(pctx, md) == 1);
+
+ ret = (EVP_PKEY_verify(pctx, sig, siglen, digest, dlen) == 1);
+
+ EVP_PKEY_CTX_free(pctx);
+ EVP_MD_CTX_free(mdctx);
+
+ return ret;
}
+#endif
bool rspamd_cryptobox_verify(const unsigned char *sig,
gsize siglen,
const unsigned char *m,
gsize mlen,
- const rspamd_pk_t pk,
- enum rspamd_cryptobox_mode mode)
+ const rspamd_sig_pk_t pk)
{
bool ret = false;
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- if (siglen == rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519)) {
- ret = (crypto_sign_verify_detached(sig, m, mlen, pk) == 0);
- }
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EC_KEY *lk;
- EC_POINT *ec_pub;
- BIGNUM *bn_pub;
- EVP_MD_CTX *sha_ctx;
- unsigned char h[64];
-
- /* Prehash */
- sha_ctx = EVP_MD_CTX_create();
- g_assert(EVP_DigestInit(sha_ctx, EVP_sha512()) == 1);
- EVP_DigestUpdate(sha_ctx, m, mlen);
- EVP_DigestFinal(sha_ctx, h, NULL);
-
- /* Key setup */
- lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- g_assert(lk != NULL);
- bn_pub = BN_bin2bn(pk, rspamd_cryptobox_pk_bytes(mode), NULL);
- g_assert(bn_pub != NULL);
- ec_pub = ec_point_bn2point_compat(EC_KEY_get0_group(lk), bn_pub, NULL, NULL);
- g_assert(ec_pub != NULL);
- g_assert(EC_KEY_set_public_key(lk, ec_pub) == 1);
-
- /* ECDSA */
- ret = ECDSA_verify(0, h, sizeof(h), sig, siglen, lk) == 1;
-
- EC_KEY_free(lk);
- EVP_MD_CTX_destroy(sha_ctx);
- BN_free(bn_pub);
- EC_POINT_free(ec_pub);
-#endif
+ if (siglen == crypto_sign_bytes()) {
+ ret = (crypto_sign_verify_detached(sig, m, mlen, pk) == 0);
}
return ret;
}
-static gsize
-rspamd_cryptobox_encrypt_ctx_len(enum rspamd_cryptobox_mode mode)
-{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return sizeof(chacha_state) + CRYPTOBOX_ALIGNMENT;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- return sizeof(EVP_CIPHER_CTX *) + CRYPTOBOX_ALIGNMENT;
-#endif
- }
-
- return 0;
-}
-
-static gsize
-rspamd_cryptobox_auth_ctx_len(enum rspamd_cryptobox_mode mode)
-{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return sizeof(crypto_onetimeauth_state) + RSPAMD_ALIGNOF(crypto_onetimeauth_state);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- return sizeof(void *);
-#endif
- }
-
- return 0;
-}
-
static void *
rspamd_cryptobox_encrypt_init(void *enc_ctx, const rspamd_nonce_t nonce,
- const rspamd_nm_t nm,
- enum rspamd_cryptobox_mode mode)
+ const rspamd_nm_t nm)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- chacha_state *s;
+ chacha_state *s;
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- xchacha_init(s,
- (const chacha_key *) nm,
- (const chacha_iv24 *) nonce,
- 20);
-
- return s;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s;
-
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- memset(s, 0, sizeof(*s));
- *s = EVP_CIPHER_CTX_new();
- g_assert(EVP_EncryptInit_ex(*s, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1);
- g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_IVLEN,
- rspamd_cryptobox_nonce_bytes(mode), NULL) == 1);
- g_assert(EVP_EncryptInit_ex(*s, NULL, NULL, nm, nonce) == 1);
-
- return s;
-#endif
- }
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ xchacha_init(s,
+ (const chacha_key *) nm,
+ (const chacha_iv24 *) nonce,
+ 20);
- return NULL;
+ return s;
}
static void *
-rspamd_cryptobox_auth_init(void *auth_ctx, void *enc_ctx,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_auth_init(void *auth_ctx, void *enc_ctx)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
- unsigned char RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
-
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- memset(subkey, 0, sizeof(subkey));
- chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
- crypto_onetimeauth_init(mac_ctx, subkey);
- rspamd_explicit_memzero(subkey, sizeof(subkey));
-
- return mac_ctx;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- auth_ctx = enc_ctx;
+ crypto_onetimeauth_state *mac_ctx;
+ unsigned char RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
- return auth_ctx;
-#endif
- }
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(subkey, 0, sizeof(subkey));
+ chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
+ crypto_onetimeauth_init(mac_ctx, subkey);
+ rspamd_explicit_memzero(subkey, sizeof(subkey));
- return NULL;
+ return mac_ctx;
}
static gboolean
rspamd_cryptobox_encrypt_update(void *enc_ctx, const unsigned char *in, gsize inlen,
- unsigned char *out, gsize *outlen,
- enum rspamd_cryptobox_mode mode)
+ unsigned char *out, gsize *outlen)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- gsize r;
- chacha_state *s;
-
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
-
- r = chacha_update(s, in, out, inlen);
-
- if (outlen != NULL) {
- *outlen = r;
- }
-
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = enc_ctx;
- int r;
+ gsize r;
+ chacha_state *s;
- r = inlen;
- g_assert(EVP_EncryptUpdate(*s, out, &r, in, inlen) == 1);
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- if (outlen) {
- *outlen = r;
- }
+ r = chacha_update(s, in, out, inlen);
- return TRUE;
-#endif
+ if (outlen != NULL) {
+ *outlen = r;
}
- return FALSE;
+ return TRUE;
}
static gboolean
-rspamd_cryptobox_auth_update(void *auth_ctx, const unsigned char *in, gsize inlen,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_auth_update(void *auth_ctx, const unsigned char *in, gsize inlen)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
+ crypto_onetimeauth_state *mac_ctx;
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- crypto_onetimeauth_update(mac_ctx, in, inlen);
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_update(mac_ctx, in, inlen);
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- return TRUE;
-#endif
- }
-
- return FALSE;
+ return TRUE;
}
static gsize
-rspamd_cryptobox_encrypt_final(void *enc_ctx, unsigned char *out, gsize remain,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_encrypt_final(void *enc_ctx, unsigned char *out, gsize remain)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- chacha_state *s;
-
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- return chacha_final(s, out);
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = enc_ctx;
- int r = remain;
+ chacha_state *s;
- g_assert(EVP_EncryptFinal_ex(*s, out, &r) == 1);
-
- return r;
-#endif
- }
-
- return 0;
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ return chacha_final(s, out);
}
static gboolean
-rspamd_cryptobox_auth_final(void *auth_ctx, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_auth_final(void *auth_ctx, rspamd_mac_t sig)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
-
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- crypto_onetimeauth_final(mac_ctx, sig);
-
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = auth_ctx;
-
- g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_GET_TAG,
- sizeof(rspamd_mac_t), sig) == 1);
+ crypto_onetimeauth_state *mac_ctx;
- return TRUE;
-#endif
- }
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_final(mac_ctx, sig);
- return FALSE;
+ return TRUE;
}
static void *
rspamd_cryptobox_decrypt_init(void *enc_ctx, const rspamd_nonce_t nonce,
- const rspamd_nm_t nm,
- enum rspamd_cryptobox_mode mode)
+ const rspamd_nm_t nm)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
-
- chacha_state *s;
+ chacha_state *s;
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- xchacha_init(s,
- (const chacha_key *) nm,
- (const chacha_iv24 *) nonce,
- 20);
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ xchacha_init(s,
+ (const chacha_key *) nm,
+ (const chacha_iv24 *) nonce,
+ 20);
- return s;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s;
-
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- memset(s, 0, sizeof(*s));
- *s = EVP_CIPHER_CTX_new();
- g_assert(EVP_DecryptInit_ex(*s, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1);
- g_assert(EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_IVLEN,
- rspamd_cryptobox_nonce_bytes(mode), NULL) == 1);
- g_assert(EVP_DecryptInit_ex(*s, NULL, NULL, nm, nonce) == 1);
-
- return s;
-#endif
- }
-
- return NULL;
+ return s;
}
static void *
-rspamd_cryptobox_auth_verify_init(void *auth_ctx, void *enc_ctx,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_auth_verify_init(void *auth_ctx, void *enc_ctx)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
- unsigned char RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
+ crypto_onetimeauth_state *mac_ctx;
+ unsigned char RSPAMD_ALIGNED(32) subkey[CHACHA_BLOCKBYTES];
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- memset(subkey, 0, sizeof(subkey));
- chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
- crypto_onetimeauth_init(mac_ctx, subkey);
- rspamd_explicit_memzero(subkey, sizeof(subkey));
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ memset(subkey, 0, sizeof(subkey));
+ chacha_update(enc_ctx, subkey, subkey, sizeof(subkey));
+ crypto_onetimeauth_init(mac_ctx, subkey);
+ rspamd_explicit_memzero(subkey, sizeof(subkey));
- return mac_ctx;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- auth_ctx = enc_ctx;
-
- return auth_ctx;
-#endif
- }
-
- return NULL;
+ return mac_ctx;
}
static gboolean
rspamd_cryptobox_decrypt_update(void *enc_ctx, const unsigned char *in, gsize inlen,
- unsigned char *out, gsize *outlen,
- enum rspamd_cryptobox_mode mode)
+ unsigned char *out, gsize *outlen)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- gsize r;
- chacha_state *s;
-
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- r = chacha_update(s, in, out, inlen);
+ gsize r;
+ chacha_state *s;
- if (outlen != NULL) {
- *outlen = r;
- }
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ r = chacha_update(s, in, out, inlen);
- return TRUE;
+ if (outlen != NULL) {
+ *outlen = r;
}
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = enc_ctx;
- int r;
-
- r = outlen ? *outlen : inlen;
- g_assert(EVP_DecryptUpdate(*s, out, &r, in, inlen) == 1);
- if (outlen) {
- *outlen = r;
- }
-
- return TRUE;
-#endif
- }
+ return TRUE;
}
static gboolean
rspamd_cryptobox_auth_verify_update(void *auth_ctx,
- const unsigned char *in, gsize inlen,
- enum rspamd_cryptobox_mode mode)
+ const unsigned char *in, gsize inlen)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
-
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- crypto_onetimeauth_update(mac_ctx, in, inlen);
+ crypto_onetimeauth_state *mac_ctx;
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- /* We do not need to authenticate as a separate process */
- return TRUE;
-#else
-#endif
- }
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_update(mac_ctx, in, inlen);
- return FALSE;
+ return TRUE;
}
static gboolean
-rspamd_cryptobox_decrypt_final(void *enc_ctx, unsigned char *out, gsize remain,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_decrypt_final(void *enc_ctx, unsigned char *out, gsize remain)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- chacha_state *s;
+ chacha_state *s;
- s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
- chacha_final(s, out);
+ s = cryptobox_align_ptr(enc_ctx, CRYPTOBOX_ALIGNMENT);
+ chacha_final(s, out);
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = enc_ctx;
- int r = remain;
-
- if (EVP_DecryptFinal_ex(*s, out, &r) < 0) {
- return FALSE;
- }
-
- return TRUE;
-#endif
- }
-
- return FALSE;
+ return TRUE;
}
static gboolean
-rspamd_cryptobox_auth_verify_final(void *auth_ctx, const rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_auth_verify_final(void *auth_ctx, const rspamd_mac_t sig)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- rspamd_mac_t mac;
- crypto_onetimeauth_state *mac_ctx;
+ rspamd_mac_t mac;
+ crypto_onetimeauth_state *mac_ctx;
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- crypto_onetimeauth_final(mac_ctx, mac);
-
- if (crypto_verify_16(mac, sig) != 0) {
- return FALSE;
- }
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ crypto_onetimeauth_final(mac_ctx, mac);
- return TRUE;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = auth_ctx;
-
- if (EVP_CIPHER_CTX_ctrl(*s, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *) sig) != 1) {
- return FALSE;
- }
-
- return TRUE;
-#endif
+ if (crypto_verify_16(mac, sig) != 0) {
+ return FALSE;
}
- return FALSE;
+ return TRUE;
}
static void
-rspamd_cryptobox_cleanup(void *enc_ctx, void *auth_ctx,
- enum rspamd_cryptobox_mode mode)
+rspamd_cryptobox_cleanup(void *enc_ctx, void *auth_ctx)
{
- if (G_LIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- crypto_onetimeauth_state *mac_ctx;
-
- mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
- rspamd_explicit_memzero(mac_ctx, sizeof(*mac_ctx));
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- EVP_CIPHER_CTX **s = enc_ctx;
+ crypto_onetimeauth_state *mac_ctx;
- EVP_CIPHER_CTX_cleanup(*s);
- EVP_CIPHER_CTX_free(*s);
-#endif
- }
+ mac_ctx = cryptobox_align_ptr(auth_ctx, CRYPTOBOX_ALIGNMENT);
+ rspamd_explicit_memzero(mac_ctx, sizeof(*mac_ctx));
}
void rspamd_cryptobox_encrypt_nm_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
const rspamd_nm_t nm,
- rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+ rspamd_mac_t sig)
{
gsize r;
void *enc_ctx, *auth_ctx;
- enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
- auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+ enc_ctx = g_alloca(sizeof(chacha_state) + CRYPTOBOX_ALIGNMENT);
+ auth_ctx = g_alloca(sizeof(crypto_onetimeauth_state) + RSPAMD_ALIGNOF(crypto_onetimeauth_state));
- enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm, mode);
- auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx, mode);
+ enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm);
+ auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx);
- rspamd_cryptobox_encrypt_update(enc_ctx, data, len, data, &r, mode);
- rspamd_cryptobox_encrypt_final(enc_ctx, data + r, len - r, mode);
+ rspamd_cryptobox_encrypt_update(enc_ctx, data, len, data, &r);
+ rspamd_cryptobox_encrypt_final(enc_ctx, data + r, len - r);
- rspamd_cryptobox_auth_update(auth_ctx, data, len, mode);
- rspamd_cryptobox_auth_final(auth_ctx, sig, mode);
+ rspamd_cryptobox_auth_update(auth_ctx, data, len);
+ rspamd_cryptobox_auth_final(auth_ctx, sig);
- rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx);
}
static void
@@ -1098,8 +655,7 @@ rspamd_cryptobox_flush_outbuf(struct rspamd_cryptobox_segment *st,
void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segments,
gsize cnt,
const rspamd_nonce_t nonce,
- const rspamd_nm_t nm, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+ const rspamd_nm_t nm, rspamd_mac_t sig)
{
struct rspamd_cryptobox_segment *cur = segments, *start_seg = segments;
unsigned char outbuf[CHACHA_BLOCKBYTES * 16];
@@ -1107,11 +663,11 @@ void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segme
unsigned char *out, *in;
gsize r, remain, inremain, seg_offset;
- enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
- auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+ enc_ctx = g_alloca(sizeof(chacha_state) + CRYPTOBOX_ALIGNMENT);
+ auth_ctx = g_alloca(sizeof(crypto_onetimeauth_state) + RSPAMD_ALIGNOF(crypto_onetimeauth_state));
- enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm, mode);
- auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx, mode);
+ enc_ctx = rspamd_cryptobox_encrypt_init(enc_ctx, nonce, nm);
+ auth_ctx = rspamd_cryptobox_auth_init(auth_ctx, enc_ctx);
remain = sizeof(outbuf);
out = outbuf;
@@ -1131,9 +687,8 @@ void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segme
if (remain == 0) {
rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf),
- outbuf, NULL, mode);
- rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf),
- mode);
+ outbuf, NULL);
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf));
rspamd_cryptobox_flush_outbuf(start_seg, outbuf,
sizeof(outbuf), seg_offset);
start_seg = cur;
@@ -1145,9 +700,8 @@ void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segme
else {
memcpy(out, cur->data, remain);
rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf),
- outbuf, NULL, mode);
- rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf),
- mode);
+ outbuf, NULL);
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf));
rspamd_cryptobox_flush_outbuf(start_seg, outbuf, sizeof(outbuf),
seg_offset);
seg_offset = 0;
@@ -1165,12 +719,10 @@ void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segme
outbuf,
sizeof(outbuf),
outbuf,
- NULL,
- mode);
+ NULL);
rspamd_cryptobox_auth_update(auth_ctx,
outbuf,
- sizeof(outbuf),
- mode);
+ sizeof(outbuf));
memcpy(in, outbuf, sizeof(outbuf));
in += sizeof(outbuf);
inremain -= sizeof(outbuf);
@@ -1190,46 +742,44 @@ void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segme
}
rspamd_cryptobox_encrypt_update(enc_ctx, outbuf, sizeof(outbuf) - remain,
- outbuf, &r, mode);
+ outbuf, &r);
out = outbuf + r;
- rspamd_cryptobox_encrypt_final(enc_ctx, out, sizeof(outbuf) - remain - r,
- mode);
+ rspamd_cryptobox_encrypt_final(enc_ctx, out, sizeof(outbuf) - remain - r);
- rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf) - remain,
- mode);
- rspamd_cryptobox_auth_final(auth_ctx, sig, mode);
+ rspamd_cryptobox_auth_update(auth_ctx, outbuf, sizeof(outbuf) - remain);
+ rspamd_cryptobox_auth_final(auth_ctx, sig);
rspamd_cryptobox_flush_outbuf(start_seg, outbuf, sizeof(outbuf) - remain,
seg_offset);
- rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx);
}
gboolean
rspamd_cryptobox_decrypt_nm_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce, const rspamd_nm_t nm,
- const rspamd_mac_t sig, enum rspamd_cryptobox_mode mode)
+ const rspamd_mac_t sig)
{
gsize r = 0;
gboolean ret = TRUE;
void *enc_ctx, *auth_ctx;
- enc_ctx = g_alloca(rspamd_cryptobox_encrypt_ctx_len(mode));
- auth_ctx = g_alloca(rspamd_cryptobox_auth_ctx_len(mode));
+ enc_ctx = g_alloca(sizeof(chacha_state) + CRYPTOBOX_ALIGNMENT);
+ auth_ctx = g_alloca(sizeof(crypto_onetimeauth_state) + RSPAMD_ALIGNOF(crypto_onetimeauth_state));
- enc_ctx = rspamd_cryptobox_decrypt_init(enc_ctx, nonce, nm, mode);
- auth_ctx = rspamd_cryptobox_auth_verify_init(auth_ctx, enc_ctx, mode);
+ enc_ctx = rspamd_cryptobox_decrypt_init(enc_ctx, nonce, nm);
+ auth_ctx = rspamd_cryptobox_auth_verify_init(auth_ctx, enc_ctx);
- rspamd_cryptobox_auth_verify_update(auth_ctx, data, len, mode);
+ rspamd_cryptobox_auth_verify_update(auth_ctx, data, len);
- if (!rspamd_cryptobox_auth_verify_final(auth_ctx, sig, mode)) {
+ if (!rspamd_cryptobox_auth_verify_final(auth_ctx, sig)) {
ret = FALSE;
}
else {
- rspamd_cryptobox_decrypt_update(enc_ctx, data, len, data, &r, mode);
- ret = rspamd_cryptobox_decrypt_final(enc_ctx, data + r, len - r, mode);
+ rspamd_cryptobox_decrypt_update(enc_ctx, data, len, data, &r);
+ ret = rspamd_cryptobox_decrypt_final(enc_ctx, data + r, len - r);
}
- rspamd_cryptobox_cleanup(enc_ctx, auth_ctx, mode);
+ rspamd_cryptobox_cleanup(enc_ctx, auth_ctx);
return ret;
}
@@ -1238,14 +788,13 @@ gboolean
rspamd_cryptobox_decrypt_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
const rspamd_pk_t pk, const rspamd_sk_t sk,
- const rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+ const rspamd_mac_t sig)
{
unsigned char nm[rspamd_cryptobox_MAX_NMBYTES];
gboolean ret;
- rspamd_cryptobox_nm(nm, pk, sk, mode);
- ret = rspamd_cryptobox_decrypt_nm_inplace(data, len, nonce, nm, sig, mode);
+ rspamd_cryptobox_nm(nm, pk, sk);
+ ret = rspamd_cryptobox_decrypt_nm_inplace(data, len, nonce, nm, sig);
rspamd_explicit_memzero(nm, sizeof(nm));
@@ -1255,13 +804,12 @@ rspamd_cryptobox_decrypt_inplace(unsigned char *data, gsize len,
void rspamd_cryptobox_encrypt_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
const rspamd_pk_t pk, const rspamd_sk_t sk,
- rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+ rspamd_mac_t sig)
{
unsigned char nm[rspamd_cryptobox_MAX_NMBYTES];
- rspamd_cryptobox_nm(nm, pk, sk, mode);
- rspamd_cryptobox_encrypt_nm_inplace(data, len, nonce, nm, sig, mode);
+ rspamd_cryptobox_nm(nm, pk, sk);
+ rspamd_cryptobox_encrypt_nm_inplace(data, len, nonce, nm, sig);
rspamd_explicit_memzero(nm, sizeof(nm));
}
@@ -1269,13 +817,12 @@ void rspamd_cryptobox_encryptv_inplace(struct rspamd_cryptobox_segment *segments
gsize cnt,
const rspamd_nonce_t nonce,
const rspamd_pk_t pk, const rspamd_sk_t sk,
- rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode)
+ rspamd_mac_t sig)
{
unsigned char nm[rspamd_cryptobox_MAX_NMBYTES];
- rspamd_cryptobox_nm(nm, pk, sk, mode);
- rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, nonce, nm, sig, mode);
+ rspamd_cryptobox_nm(nm, pk, sk);
+ rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, nonce, nm, sig);
rspamd_explicit_memzero(nm, sizeof(nm));
}
@@ -1288,9 +835,9 @@ void rspamd_cryptobox_siphash(unsigned char *out, const unsigned char *in,
}
/*
- * Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
- * Code based on IEEE Std 802.11-2007, Annex H.4.2.
- */
+* Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
+* Code based on IEEE Std 802.11-2007, Annex H.4.2.
+*/
static gboolean
rspamd_cryptobox_pbkdf2(const char *pass, gsize pass_len,
const uint8_t *salt, gsize salt_len, uint8_t *key, gsize key_len,
@@ -1327,9 +874,9 @@ rspamd_cryptobox_pbkdf2(const char *pass, gsize pass_len,
uint8_t k[crypto_generichash_blake2b_BYTES_MAX];
/*
- * We use additional blake2 iteration to store large key
- * XXX: it is not compatible with the original implementation but safe
- */
+ * We use additional blake2 iteration to store large key
+ * XXX: it is not compatible with the original implementation but safe
+ */
crypto_generichash_blake2b(k, sizeof(k), pass, pass_len,
NULL, 0);
crypto_generichash_blake2b(d1, sizeof(d1), asalt, salt_len + 4,
@@ -1347,9 +894,9 @@ rspamd_cryptobox_pbkdf2(const char *pass, gsize pass_len,
uint8_t k[crypto_generichash_blake2b_BYTES_MAX];
/*
- * We use additional blake2 iteration to store large key
- * XXX: it is not compatible with the original implementation but safe
- */
+ * We use additional blake2 iteration to store large key
+ * XXX: it is not compatible with the original implementation but safe
+ */
crypto_generichash_blake2b(k, sizeof(k), pass, pass_len,
NULL, 0);
crypto_generichash_blake2b(d2, sizeof(d2), d1, sizeof(d1),
@@ -1402,84 +949,6 @@ rspamd_cryptobox_pbkdf(const char *pass, gsize pass_len,
return ret;
}
-unsigned int rspamd_cryptobox_pk_bytes(enum rspamd_cryptobox_mode mode)
-{
- if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return 32;
- }
- else {
- return 65;
- }
-}
-
-unsigned int rspamd_cryptobox_pk_sig_bytes(enum rspamd_cryptobox_mode mode)
-{
- if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return 32;
- }
- else {
- return 65;
- }
-}
-
-unsigned int rspamd_cryptobox_nonce_bytes(enum rspamd_cryptobox_mode mode)
-{
- if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return 24;
- }
- else {
- return 16;
- }
-}
-
-
-unsigned int rspamd_cryptobox_sk_bytes(enum rspamd_cryptobox_mode mode)
-{
- return 32;
-}
-
-unsigned int rspamd_cryptobox_sk_sig_bytes(enum rspamd_cryptobox_mode mode)
-{
- if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return 64;
- }
- else {
- return 32;
- }
-}
-
-unsigned int rspamd_cryptobox_signature_bytes(enum rspamd_cryptobox_mode mode)
-{
- static unsigned int ssl_keylen;
-
- if (G_UNLIKELY(mode == RSPAMD_CRYPTOBOX_MODE_25519)) {
- return 64;
- }
- else {
-#ifndef HAVE_USABLE_OPENSSL
- g_assert(0);
-#else
- if (ssl_keylen == 0) {
- EC_KEY *lk;
- lk = EC_KEY_new_by_curve_name(CRYPTOBOX_CURVE_NID);
- ssl_keylen = ECDSA_size(lk);
- EC_KEY_free(lk);
- }
-#endif
- return ssl_keylen;
- }
-}
-
-unsigned int rspamd_cryptobox_nm_bytes(enum rspamd_cryptobox_mode mode)
-{
- return 32;
-}
-
-unsigned int rspamd_cryptobox_mac_bytes(enum rspamd_cryptobox_mode mode)
-{
- return 16;
-}
-
void rspamd_cryptobox_hash_init(rspamd_cryptobox_hash_state_t *p, const unsigned char *key, gsize keylen)
{
crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
@@ -1489,8 +958,8 @@ void rspamd_cryptobox_hash_init(rspamd_cryptobox_hash_state_t *p, const unsigned
}
/**
- * Update hash with data portion
- */
+* Update hash with data portion
+*/
void rspamd_cryptobox_hash_update(rspamd_cryptobox_hash_state_t *p, const unsigned char *data, gsize len)
{
crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
@@ -1499,8 +968,8 @@ void rspamd_cryptobox_hash_update(rspamd_cryptobox_hash_state_t *p, const unsign
}
/**
- * Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
- */
+* Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
+*/
void rspamd_cryptobox_hash_final(rspamd_cryptobox_hash_state_t *p, unsigned char *out)
{
crypto_generichash_blake2b_state *st = cryptobox_align_ptr(p,
@@ -1509,8 +978,8 @@ void rspamd_cryptobox_hash_final(rspamd_cryptobox_hash_state_t *p, unsigned char
}
/**
- * One in all function
- */
+* One in all function
+*/
void rspamd_cryptobox_hash(unsigned char *out,
const unsigned char *data,
gsize len,
@@ -1729,8 +1198,8 @@ rspamd_cryptobox_fast_hash_final(rspamd_cryptobox_fast_hash_state_t *st)
}
/**
- * One in all function
- */
+* One in all function
+*/
static inline uint64_t
rspamd_cryptobox_fast_hash_machdep(const void *data,
gsize len, uint64_t seed)
diff --git a/src/libcryptobox/cryptobox.h b/src/libcryptobox/cryptobox.h
index 8382c8f68..afe9c4f9a 100644
--- a/src/libcryptobox/cryptobox.h
+++ b/src/libcryptobox/cryptobox.h
@@ -13,11 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#ifndef CRYPTOBOX_H_
#define CRYPTOBOX_H_
#include "config.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include <sodium.h>
#ifdef __cplusplus
@@ -35,18 +40,18 @@ struct rspamd_cryptobox_segment {
#define RSPAMD_HAS_TARGET_ATTR 1
#endif
-#define rspamd_cryptobox_MAX_NONCEBYTES 24
-#define rspamd_cryptobox_MAX_PKBYTES 65
-#define rspamd_cryptobox_MAX_SKBYTES 32
-#define rspamd_cryptobox_MAX_MACBYTES 16
-#define rspamd_cryptobox_MAX_NMBYTES 32
+#define rspamd_cryptobox_MAX_NONCEBYTES crypto_box_NONCEBYTES
+#define rspamd_cryptobox_MAX_PKBYTES crypto_box_PUBLICKEYBYTES
+#define rspamd_cryptobox_MAX_SKBYTES crypto_box_SECRETKEYBYTES
+#define rspamd_cryptobox_MAX_MACBYTES crypto_box_MACBYTES
+#define rspamd_cryptobox_MAX_NMBYTES crypto_box_BEFORENMBYTES
#define rspamd_cryptobox_SIPKEYBYTES 16
#define rspamd_cryptobox_HASHBYTES 64
#define rspamd_cryptobox_HASHKEYBYTES 64
#define rspamd_cryptobox_HASHSTATEBYTES sizeof(crypto_generichash_blake2b_state) + 64
-#define rspamd_cryptobox_MAX_SIGSKBYTES 64
-#define rspamd_cryptobox_MAX_SIGPKBYTES 32
-#define rspamd_cryptobox_MAX_SIGBYTES 72
+#define rspamd_cryptobox_MAX_SIGSKBYTES crypto_sign_SECRETKEYBYTES
+#define rspamd_cryptobox_MAX_SIGPKBYTES crypto_sign_PUBLICKEYBYTES
+#define rspamd_cryptobox_MAX_SIGBYTES crypto_sign_BYTES
#define CPUID_AVX2 0x1
#define CPUID_AVX 0x2
@@ -67,10 +72,6 @@ typedef unsigned char rspamd_signature_t[rspamd_cryptobox_MAX_SIGBYTES];
typedef unsigned char rspamd_sig_pk_t[rspamd_cryptobox_MAX_SIGPKBYTES];
typedef unsigned char rspamd_sig_sk_t[rspamd_cryptobox_MAX_SIGSKBYTES];
-enum rspamd_cryptobox_mode {
- RSPAMD_CRYPTOBOX_MODE_25519 = 0,
- RSPAMD_CRYPTOBOX_MODE_NIST
-};
struct rspamd_cryptobox_library_ctx {
char *cpu_extensions;
@@ -80,172 +81,190 @@ struct rspamd_cryptobox_library_ctx {
};
/**
- * Init cryptobox library
- */
+* Init cryptobox library
+*/
struct rspamd_cryptobox_library_ctx *rspamd_cryptobox_init(void);
void rspamd_cryptobox_deinit(struct rspamd_cryptobox_library_ctx *);
/**
- * Generate new keypair
- * @param pk public key buffer
- * @param sk secret key buffer
- */
-void rspamd_cryptobox_keypair(rspamd_pk_t pk, rspamd_sk_t sk,
- enum rspamd_cryptobox_mode mode);
+* Generate new keypair
+* @param pk public key buffer
+* @param sk secret key buffer
+*/
+void rspamd_cryptobox_keypair(rspamd_pk_t pk, rspamd_sk_t sk);
/**
- * Generate new keypair for signing
- * @param pk public key buffer
- * @param sk secret key buffer
- */
-void rspamd_cryptobox_keypair_sig(rspamd_sig_pk_t pk, rspamd_sig_sk_t sk,
- enum rspamd_cryptobox_mode mode);
+* Generate new keypair for signing
+* @param pk public key buffer
+* @param sk secret key buffer
+*/
+void rspamd_cryptobox_keypair_sig(rspamd_sig_pk_t pk, rspamd_sig_sk_t sk);
/**
- * Encrypt data inplace adding signature to sig afterwards
- * @param data input buffer
- * @param pk remote pubkey
- * @param sk local secret key
- * @param sig output signature
- */
+* Encrypt data inplace adding signature to sig afterwards
+* @param data input buffer
+* @param pk remote pubkey
+* @param sk local secret key
+* @param sig output signature
+*/
void rspamd_cryptobox_encrypt_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
- const rspamd_pk_t pk, const rspamd_sk_t sk, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_pk_t pk, const rspamd_sk_t sk, rspamd_mac_t sig);
/**
- * Encrypt segments of data inplace adding signature to sig afterwards
- * @param segments segments of data
- * @param cnt count of segments
- * @param pk remote pubkey
- * @param sk local secret key
- * @param sig output signature
- */
+* Encrypt segments of data inplace adding signature to sig afterwards
+* @param segments segments of data
+* @param cnt count of segments
+* @param pk remote pubkey
+* @param sk local secret key
+* @param sig output signature
+*/
void rspamd_cryptobox_encryptv_inplace(struct rspamd_cryptobox_segment *segments,
gsize cnt,
const rspamd_nonce_t nonce,
- const rspamd_pk_t pk, const rspamd_sk_t sk, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_pk_t pk, const rspamd_sk_t sk, rspamd_mac_t sig);
/**
- * Decrypt and verify data chunk inplace
- * @param data data to decrypt
- * @param len length of data
- * @param pk remote pubkey
- * @param sk local privkey
- * @param sig signature input
- * @return TRUE if input has been verified successfully
- */
+* Decrypt and verify data chunk inplace
+* @param data data to decrypt
+* @param len length of data
+* @param pk remote pubkey
+* @param sk local privkey
+* @param sig signature input
+* @return TRUE if input has been verified successfully
+*/
gboolean rspamd_cryptobox_decrypt_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
- const rspamd_pk_t pk, const rspamd_sk_t sk, const rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_pk_t pk, const rspamd_sk_t sk, const rspamd_mac_t sig);
/**
- * Encrypt segments of data inplace adding signature to sig afterwards
- * @param segments segments of data
- * @param cnt count of segments
- * @param pk remote pubkey
- * @param sk local secret key
- * @param sig output signature
- */
+* Encrypt segments of data inplace adding signature to sig afterwards
+* @param segments segments of data
+* @param cnt count of segments
+* @param pk remote pubkey
+* @param sk local secret key
+* @param sig output signature
+*/
void rspamd_cryptobox_encrypt_nm_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
- const rspamd_nm_t nm, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_nm_t nm, rspamd_mac_t sig);
/**
- * Encrypt segments of data inplace adding signature to sig afterwards
- * @param segments segments of data
- * @param cnt count of segments
- * @param pk remote pubkey
- * @param sk local secret key
- * @param sig output signature
- */
+* Encrypt segments of data inplace adding signature to sig afterwards
+* @param segments segments of data
+* @param cnt count of segments
+* @param pk remote pubkey
+* @param sk local secret key
+* @param sig output signature
+*/
void rspamd_cryptobox_encryptv_nm_inplace(struct rspamd_cryptobox_segment *segments,
gsize cnt,
const rspamd_nonce_t nonce,
- const rspamd_nm_t nm, rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_nm_t nm, rspamd_mac_t sig);
/**
- * Decrypt and verify data chunk inplace
- * @param data data to decrypt
- * @param len length of data
- * @param pk remote pubkey
- * @param sk local privkey
- * @param sig signature input
- * @return TRUE if input has been verified successfully
- */
+* Decrypt and verify data chunk inplace
+* @param data data to decrypt
+* @param len length of data
+* @param pk remote pubkey
+* @param sk local privkey
+* @param sig signature input
+* @return TRUE if input has been verified successfully
+*/
gboolean rspamd_cryptobox_decrypt_nm_inplace(unsigned char *data, gsize len,
const rspamd_nonce_t nonce,
- const rspamd_nm_t nm, const rspamd_mac_t sig,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_nm_t nm, const rspamd_mac_t sig);
/**
- * Generate shared secret from local sk and remote pk
- * @param nm shared secret
- * @param pk remote pubkey
- * @param sk local privkey
- */
-void rspamd_cryptobox_nm(rspamd_nm_t nm, const rspamd_pk_t pk,
- const rspamd_sk_t sk, enum rspamd_cryptobox_mode mode);
+* Generate shared secret from local sk and remote pk
+* @param nm shared secret
+* @param pk remote pubkey
+* @param sk local privkey
+*/
+void rspamd_cryptobox_nm(rspamd_nm_t nm, const rspamd_pk_t pk, const rspamd_sk_t sk);
/**
- * Create digital signature for the specified message and place result in `sig`
- * @param sig signature target
- * @param siglen_p pointer to signature length (might be NULL)
- * @param m input message
- * @param mlen input length
- * @param sk secret key
- */
+* Create digital signature for the specified message and place result in `sig`
+* @param sig signature target
+* @param siglen_p pointer to signature length (might be NULL)
+* @param m input message
+* @param mlen input length
+* @param sk secret key
+*/
void rspamd_cryptobox_sign(unsigned char *sig, unsigned long long *siglen_p,
const unsigned char *m, gsize mlen,
- const rspamd_sk_t sk,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_sig_sk_t sk);
/**
- * Verifies digital signature for the specified message using the specified
- * pubkey
- * @param sig signature source
- * @param m input message
- * @param mlen message length
- * @param pk public key for verification
- * @return true if signature is valid, false otherwise
- */
+* Verifies digital signature for the specified message using the specified
+* pubkey
+* @param sig signature source
+* @param m input message
+* @param mlen message length
+* @param pk public key for verification
+* @return true if signature is valid, false otherwise
+*/
bool rspamd_cryptobox_verify(const unsigned char *sig,
gsize siglen,
const unsigned char *m,
gsize mlen,
- const rspamd_pk_t pk,
- enum rspamd_cryptobox_mode mode);
+ const rspamd_sig_pk_t pk);
+#ifdef HAVE_OPENSSL
/**
- * Securely clear the buffer specified
- * @param buf buffer to zero
- * @param buflen length of buffer
+ * Verifies digital signature for specified raw digest with specified pubkey
+ * @param nid signing algorithm nid
+ * @param sig signature source
+ * @param digest raw digest
+ * @param pub_key public key for verification
+ * @return true if signature is valid, false otherwise
*/
+bool rspamd_cryptobox_verify_evp_ed25519(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ EVP_PKEY *pub_key);
+bool rspamd_cryptobox_verify_evp_ecdsa(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ EVP_PKEY *pub_key);
+bool rspamd_cryptobox_verify_evp_rsa(int nid,
+ const unsigned char *sig,
+ gsize siglen,
+ const unsigned char *digest,
+ gsize dlen,
+ EVP_PKEY *pub_key);
+#endif
+
+/**
+* Securely clear the buffer specified
+* @param buf buffer to zero
+* @param buflen length of buffer
+*/
#define rspamd_explicit_memzero sodium_memzero
/**
- * Constant time memcmp
- * @param b1_
- * @param b2_
- * @param len
- * @return
- */
+* Constant time memcmp
+* @param b1_
+* @param b2_
+* @param len
+* @return
+*/
#define rspamd_cryptobox_memcmp sodium_memcmp
/**
- * Calculates siphash-2-4 for a message
- * @param out (8 bytes output)
- * @param in
- * @param inlen
- * @param k key (must be 16 bytes)
- */
+* Calculates siphash-2-4 for a message
+* @param out (8 bytes output)
+* @param in
+* @param inlen
+* @param k key (must be 16 bytes)
+*/
void rspamd_cryptobox_siphash(unsigned char *out, const unsigned char *in,
unsigned long long inlen,
const rspamd_sipkey_t k);
@@ -257,16 +276,16 @@ enum rspamd_cryptobox_pbkdf_type {
/**
- * Derive key from password using the specified algorithm
- * @param pass input password
- * @param pass_len length of the password
- * @param salt input salt
- * @param salt_len length of salt
- * @param key output key
- * @param key_len size of the key
- * @param complexity empiric number of complexity (rounds for pbkdf2 and garlic for catena)
- * @return TRUE in case of success and FALSE if failed
- */
+* Derive key from password using the specified algorithm
+* @param pass input password
+* @param pass_len length of the password
+* @param salt input salt
+* @param salt_len length of salt
+* @param key output key
+* @param key_len size of the key
+* @param complexity empiric number of complexity (rounds for pbkdf2 and garlic for catena)
+* @return TRUE in case of success and FALSE if failed
+*/
gboolean rspamd_cryptobox_pbkdf(const char *pass, gsize pass_len,
const uint8_t *salt, gsize salt_len,
uint8_t *key, gsize key_len,
@@ -274,71 +293,31 @@ gboolean rspamd_cryptobox_pbkdf(const char *pass, gsize pass_len,
enum rspamd_cryptobox_pbkdf_type type);
-/**
- * Real size of rspamd cryptobox public key
- */
-unsigned int rspamd_cryptobox_pk_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox signing public key
- */
-unsigned int rspamd_cryptobox_pk_sig_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of crypto nonce
- */
-unsigned int rspamd_cryptobox_nonce_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox secret key
- */
-unsigned int rspamd_cryptobox_sk_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox signing secret key
- */
-unsigned int rspamd_cryptobox_sk_sig_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox shared key
- */
-unsigned int rspamd_cryptobox_nm_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox MAC signature
- */
-unsigned int rspamd_cryptobox_mac_bytes(enum rspamd_cryptobox_mode mode);
-
-/**
- * Real size of rspamd cryptobox digital signature
- */
-unsigned int rspamd_cryptobox_signature_bytes(enum rspamd_cryptobox_mode mode);
-
/* Hash IUF interface */
typedef crypto_generichash_blake2b_state rspamd_cryptobox_hash_state_t;
/**
- * Init cryptobox hash state using key if needed, `st` must point to the buffer
- * with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
- * non-keyed hash is generated
- */
+* Init cryptobox hash state using key if needed, `st` must point to the buffer
+* with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
+* non-keyed hash is generated
+*/
void rspamd_cryptobox_hash_init(rspamd_cryptobox_hash_state_t *st,
const unsigned char *key, gsize keylen);
/**
- * Update hash with data portion
- */
+* Update hash with data portion
+*/
void rspamd_cryptobox_hash_update(rspamd_cryptobox_hash_state_t *st,
const unsigned char *data, gsize len);
/**
- * Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
- */
+* Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
+*/
void rspamd_cryptobox_hash_final(rspamd_cryptobox_hash_state_t *st, unsigned char *out);
/**
- * One in all function
- */
+* One in all function
+*/
void rspamd_cryptobox_hash(unsigned char *out,
const unsigned char *data,
gsize len,
@@ -363,71 +342,71 @@ typedef struct CRYPTO_ALIGN(64) rspamd_cryptobox_fast_hash_state_s {
/**
- * Creates a new cryptobox state properly aligned
- * @return
- */
+* Creates a new cryptobox state properly aligned
+* @return
+*/
rspamd_cryptobox_fast_hash_state_t *rspamd_cryptobox_fast_hash_new(void);
void rspamd_cryptobox_fast_hash_free(rspamd_cryptobox_fast_hash_state_t *st);
/**
- * Init cryptobox hash state using key if needed, `st` must point to the buffer
- * with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
- * non-keyed hash is generated
- */
+* Init cryptobox hash state using key if needed, `st` must point to the buffer
+* with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
+* non-keyed hash is generated
+*/
void rspamd_cryptobox_fast_hash_init(rspamd_cryptobox_fast_hash_state_t *st,
uint64_t seed);
/**
- * Init cryptobox hash state using key if needed, `st` must point to the buffer
- * with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
- * non-keyed hash is generated
- */
+* Init cryptobox hash state using key if needed, `st` must point to the buffer
+* with at least rspamd_cryptobox_HASHSTATEBYTES bytes length. If keylen == 0, then
+* non-keyed hash is generated
+*/
void rspamd_cryptobox_fast_hash_init_specific(rspamd_cryptobox_fast_hash_state_t *st,
enum rspamd_cryptobox_fast_hash_type type,
uint64_t seed);
/**
- * Update hash with data portion
- */
+* Update hash with data portion
+*/
void rspamd_cryptobox_fast_hash_update(rspamd_cryptobox_fast_hash_state_t *st,
const void *data, gsize len);
/**
- * Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
- */
+* Output hash to the buffer of rspamd_cryptobox_HASHBYTES length
+*/
uint64_t rspamd_cryptobox_fast_hash_final(rspamd_cryptobox_fast_hash_state_t *st);
/**
- * One in all function
- */
+* One in all function
+*/
uint64_t rspamd_cryptobox_fast_hash(const void *data,
gsize len, uint64_t seed);
/**
- * Platform independent version
- */
+* Platform independent version
+*/
uint64_t rspamd_cryptobox_fast_hash_specific(
enum rspamd_cryptobox_fast_hash_type type,
const void *data,
gsize len, uint64_t seed);
/**
- * Decode base64 using platform optimized code
- * @param in
- * @param inlen
- * @param out
- * @param outlen
- * @return
- */
+* Decode base64 using platform optimized code
+* @param in
+* @param inlen
+* @param out
+* @param outlen
+* @return
+*/
gboolean rspamd_cryptobox_base64_decode(const char *in, gsize inlen,
unsigned char *out, gsize *outlen);
/**
- * Returns TRUE if data looks like a valid base64 string
- * @param in
- * @param inlen
- * @return
- */
+* Returns TRUE if data looks like a valid base64 string
+* @param in
+* @param inlen
+* @return
+*/
gboolean rspamd_cryptobox_base64_is_valid(const char *in, gsize inlen);
#ifdef __cplusplus
diff --git a/src/libcryptobox/keypair.c b/src/libcryptobox/keypair.c
index 02070bb46..d3f81ee2d 100644
--- a/src/libcryptobox/keypair.c
+++ b/src/libcryptobox/keypair.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,29 +38,14 @@ rspamd_cryptobox_keypair_sk(struct rspamd_cryptobox_keypair *kp,
{
g_assert(kp != NULL);
- if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 32;
- return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->sk;
- }
- else {
- *len = 64;
- return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->sk;
- }
+ if (kp->type == RSPAMD_KEYPAIR_KEX) {
+ *len = 32;
+ return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->sk;
}
else {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 32;
- return RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp)->sk;
- }
- else {
- *len = 32;
- return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_NIST(kp)->sk;
- }
+ *len = 64;
+ return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->sk;
}
-
- /* Not reached */
- return NULL;
}
static void *
@@ -69,29 +54,14 @@ rspamd_cryptobox_keypair_pk(struct rspamd_cryptobox_keypair *kp,
{
g_assert(kp != NULL);
- if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 32;
- return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->pk;
- }
- else {
- *len = 32;
- return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->pk;
- }
+ if (kp->type == RSPAMD_KEYPAIR_KEX) {
+ *len = 32;
+ return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->pk;
}
else {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 65;
- return RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp)->pk;
- }
- else {
- *len = 65;
- return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_NIST(kp)->pk;
- }
+ *len = 32;
+ return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->pk;
}
-
- /* Not reached */
- return NULL;
}
static void *
@@ -100,53 +70,27 @@ rspamd_cryptobox_pubkey_pk(const struct rspamd_cryptobox_pubkey *kp,
{
g_assert(kp != NULL);
- if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 32;
- return RSPAMD_CRYPTOBOX_PUBKEY_25519(kp)->pk;
- }
- else {
- *len = 32;
- return RSPAMD_CRYPTOBOX_PUBKEY_SIG_25519(kp)->pk;
- }
+ if (kp->type == RSPAMD_KEYPAIR_KEX) {
+ *len = 32;
+ return RSPAMD_CRYPTOBOX_PUBKEY_25519(kp)->pk;
}
else {
- if (kp->type == RSPAMD_KEYPAIR_KEX) {
- *len = 65;
- return RSPAMD_CRYPTOBOX_PUBKEY_NIST(kp)->pk;
- }
- else {
- *len = 65;
- return RSPAMD_CRYPTOBOX_PUBKEY_SIG_NIST(kp)->pk;
- }
+ *len = 32;
+ return RSPAMD_CRYPTOBOX_PUBKEY_SIG_25519(kp)->pk;
}
-
- /* Not reached */
- return NULL;
}
static struct rspamd_cryptobox_keypair *
-rspamd_cryptobox_keypair_alloc(enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+rspamd_cryptobox_keypair_alloc(enum rspamd_cryptobox_keypair_type type)
{
struct rspamd_cryptobox_keypair *kp;
unsigned int size = 0;
- if (alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- if (type == RSPAMD_KEYPAIR_KEX) {
- size = sizeof(struct rspamd_cryptobox_keypair_25519);
- }
- else {
- size = sizeof(struct rspamd_cryptobox_keypair_sig_25519);
- }
+ if (type == RSPAMD_KEYPAIR_KEX) {
+ size = sizeof(struct rspamd_cryptobox_keypair_25519);
}
else {
- if (type == RSPAMD_KEYPAIR_KEX) {
- size = sizeof(struct rspamd_cryptobox_keypair_nist);
- }
- else {
- size = sizeof(struct rspamd_cryptobox_keypair_sig_nist);
- }
+ size = sizeof(struct rspamd_cryptobox_keypair_sig_25519);
}
g_assert(size >= sizeof(*kp));
@@ -161,27 +105,17 @@ rspamd_cryptobox_keypair_alloc(enum rspamd_cryptobox_keypair_type type,
}
static struct rspamd_cryptobox_pubkey *
-rspamd_cryptobox_pubkey_alloc(enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+rspamd_cryptobox_pubkey_alloc(enum rspamd_cryptobox_keypair_type type)
{
struct rspamd_cryptobox_pubkey *pk;
unsigned int size = 0;
- if (alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- if (type == RSPAMD_KEYPAIR_KEX) {
- size = sizeof(struct rspamd_cryptobox_pubkey_25519);
- }
- else {
- size = sizeof(struct rspamd_cryptobox_pubkey_sig_25519);
- }
+
+ if (type == RSPAMD_KEYPAIR_KEX) {
+ size = sizeof(struct rspamd_cryptobox_pubkey_25519);
}
else {
- if (type == RSPAMD_KEYPAIR_KEX) {
- size = sizeof(struct rspamd_cryptobox_pubkey_nist);
- }
- else {
- size = sizeof(struct rspamd_cryptobox_pubkey_sig_nist);
- }
+ size = sizeof(struct rspamd_cryptobox_pubkey_sig_25519);
}
g_assert(size >= sizeof(*pk));
@@ -230,25 +164,23 @@ void rspamd_cryptobox_pubkey_dtor(struct rspamd_cryptobox_pubkey *p)
}
struct rspamd_cryptobox_keypair *
-rspamd_keypair_new(enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+rspamd_keypair_new(enum rspamd_cryptobox_keypair_type type)
{
struct rspamd_cryptobox_keypair *kp;
void *pk, *sk;
unsigned int size;
- kp = rspamd_cryptobox_keypair_alloc(type, alg);
- kp->alg = alg;
+ kp = rspamd_cryptobox_keypair_alloc(type);
kp->type = type;
sk = rspamd_cryptobox_keypair_sk(kp, &size);
pk = rspamd_cryptobox_keypair_pk(kp, &size);
if (type == RSPAMD_KEYPAIR_KEX) {
- rspamd_cryptobox_keypair(pk, sk, alg);
+ rspamd_cryptobox_keypair(pk, sk);
}
else {
- rspamd_cryptobox_keypair_sig(pk, sk, alg);
+ rspamd_cryptobox_keypair_sig(pk, sk);
}
rspamd_cryptobox_hash(kp->id, pk, size, NULL, 0);
@@ -302,27 +234,10 @@ rspamd_pubkey_type(struct rspamd_cryptobox_pubkey *p)
}
-enum rspamd_cryptobox_mode
-rspamd_keypair_alg(struct rspamd_cryptobox_keypair *kp)
-{
- g_assert(kp != NULL);
-
- return kp->alg;
-}
-
-enum rspamd_cryptobox_mode
-rspamd_pubkey_alg(struct rspamd_cryptobox_pubkey *p)
-{
- g_assert(p != NULL);
-
- return p->alg;
-}
-
struct rspamd_cryptobox_pubkey *
rspamd_pubkey_from_base32(const char *b32,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+ enum rspamd_cryptobox_keypair_type type)
{
unsigned char *decoded;
gsize dlen, expected_len;
@@ -342,16 +257,15 @@ rspamd_pubkey_from_base32(const char *b32,
return NULL;
}
- expected_len = (type == RSPAMD_KEYPAIR_KEX) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg);
+ expected_len = (type == RSPAMD_KEYPAIR_KEX) ? crypto_box_PUBLICKEYBYTES : crypto_sign_PUBLICKEYBYTES;
if (dlen != expected_len) {
g_free(decoded);
return NULL;
}
- pk = rspamd_cryptobox_pubkey_alloc(type, alg);
+ pk = rspamd_cryptobox_pubkey_alloc(type);
REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor);
- pk->alg = alg;
pk->type = type;
pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen);
@@ -365,8 +279,7 @@ rspamd_pubkey_from_base32(const char *b32,
struct rspamd_cryptobox_pubkey *
rspamd_pubkey_from_hex(const char *hex,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+ enum rspamd_cryptobox_keypair_type type)
{
unsigned char *decoded;
gsize dlen, expected_len;
@@ -388,16 +301,15 @@ rspamd_pubkey_from_hex(const char *hex,
return NULL;
}
- expected_len = (type == RSPAMD_KEYPAIR_KEX) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg);
+ expected_len = (type == RSPAMD_KEYPAIR_KEX) ? crypto_box_PUBLICKEYBYTES : crypto_sign_PUBLICKEYBYTES;
if (dlen != expected_len) {
g_free(decoded);
return NULL;
}
- pk = rspamd_cryptobox_pubkey_alloc(type, alg);
+ pk = rspamd_cryptobox_pubkey_alloc(type);
REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor);
- pk->alg = alg;
pk->type = type;
pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen);
@@ -411,25 +323,20 @@ rspamd_pubkey_from_hex(const char *hex,
struct rspamd_cryptobox_pubkey *
rspamd_pubkey_from_bin(const unsigned char *raw,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg)
+ enum rspamd_cryptobox_keypair_type type)
{
- gsize expected_len;
unsigned int pklen;
struct rspamd_cryptobox_pubkey *pk;
unsigned char *pk_data;
g_assert(raw != NULL && len > 0);
- expected_len = (type == RSPAMD_KEYPAIR_KEX) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg);
-
- if (len != expected_len) {
+ if (len != crypto_box_PUBLICKEYBYTES) {
return NULL;
}
- pk = rspamd_cryptobox_pubkey_alloc(type, alg);
+ pk = rspamd_cryptobox_pubkey_alloc(type);
REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor);
- pk->alg = alg;
pk->type = type;
pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen);
@@ -463,7 +370,6 @@ const unsigned char *
rspamd_pubkey_calculate_nm(struct rspamd_cryptobox_pubkey *p,
struct rspamd_cryptobox_keypair *kp)
{
- g_assert(kp->alg == p->alg);
g_assert(kp->type == p->type);
g_assert(p->type == RSPAMD_KEYPAIR_KEX);
@@ -476,22 +382,12 @@ rspamd_pubkey_calculate_nm(struct rspamd_cryptobox_pubkey *p,
REF_INIT_RETAIN(p->nm, rspamd_cryptobox_nm_dtor);
}
- if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- struct rspamd_cryptobox_pubkey_25519 *rk_25519 =
- RSPAMD_CRYPTOBOX_PUBKEY_25519(p);
- struct rspamd_cryptobox_keypair_25519 *sk_25519 =
- RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp);
+ struct rspamd_cryptobox_pubkey_25519 *rk_25519 =
+ RSPAMD_CRYPTOBOX_PUBKEY_25519(p);
+ struct rspamd_cryptobox_keypair_25519 *sk_25519 =
+ RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp);
- rspamd_cryptobox_nm(p->nm->nm, rk_25519->pk, sk_25519->sk, p->alg);
- }
- else {
- struct rspamd_cryptobox_pubkey_nist *rk_nist =
- RSPAMD_CRYPTOBOX_PUBKEY_NIST(p);
- struct rspamd_cryptobox_keypair_nist *sk_nist =
- RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp);
-
- rspamd_cryptobox_nm(p->nm->nm, rk_nist->pk, sk_nist->sk, p->alg);
- }
+ rspamd_cryptobox_nm(p->nm->nm, rk_25519->pk, sk_25519->sk);
return p->nm->nm;
}
@@ -662,7 +558,6 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj)
const ucl_object_t *privkey, *pubkey, *elt;
const char *str;
enum rspamd_cryptobox_keypair_type type = RSPAMD_KEYPAIR_KEX;
- enum rspamd_cryptobox_mode mode = RSPAMD_CRYPTOBOX_MODE_25519;
gboolean is_hex = FALSE;
struct rspamd_cryptobox_keypair *kp;
unsigned int len;
@@ -705,19 +600,6 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj)
/* TODO: handle errors */
}
- elt = ucl_object_lookup(obj, "algorithm");
- if (elt && ucl_object_type(elt) == UCL_STRING) {
- str = ucl_object_tostring(elt);
-
- if (g_ascii_strcasecmp(str, "curve25519") == 0) {
- mode = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else if (g_ascii_strcasecmp(str, "nistp256") == 0) {
- mode = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- /* TODO: handle errors */
- }
-
elt = ucl_object_lookup(obj, "encoding");
if (elt && ucl_object_type(elt) == UCL_STRING) {
str = ucl_object_tostring(elt);
@@ -728,9 +610,8 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj)
/* TODO: handle errors */
}
- kp = rspamd_cryptobox_keypair_alloc(type, mode);
+ kp = rspamd_cryptobox_keypair_alloc(type);
kp->type = type;
- kp->alg = mode;
REF_INIT_RETAIN(kp, rspamd_cryptobox_keypair_dtor);
g_assert(kp != NULL);
@@ -838,8 +719,7 @@ rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp,
"encoding", 0, false);
ucl_object_insert_key(elt,
- ucl_object_fromstring(
- kp->alg == RSPAMD_CRYPTOBOX_MODE_NIST ? "nistp256" : "curve25519"),
+ ucl_object_fromstring("curve25519"),
"algorithm", 0, false);
ucl_object_insert_key(elt,
@@ -873,9 +753,9 @@ rspamd_keypair_decrypt(struct rspamd_cryptobox_keypair *kp,
return FALSE;
}
- if (inlen < sizeof(encrypted_magic) + rspamd_cryptobox_pk_bytes(kp->alg) +
- rspamd_cryptobox_mac_bytes(kp->alg) +
- rspamd_cryptobox_nonce_bytes(kp->alg)) {
+ if (inlen < sizeof(encrypted_magic) + crypto_box_publickeybytes() +
+ crypto_box_macbytes() +
+ crypto_box_noncebytes()) {
g_set_error(err, rspamd_keypair_quark(), E2BIG, "invalid size: too small");
return FALSE;
@@ -890,9 +770,9 @@ rspamd_keypair_decrypt(struct rspamd_cryptobox_keypair *kp,
/* Set pointers */
pubkey = in + sizeof(encrypted_magic);
- mac = pubkey + rspamd_cryptobox_pk_bytes(kp->alg);
- nonce = mac + rspamd_cryptobox_mac_bytes(kp->alg);
- data = nonce + rspamd_cryptobox_nonce_bytes(kp->alg);
+ mac = pubkey + crypto_box_publickeybytes();
+ nonce = mac + crypto_box_macbytes();
+ data = nonce + crypto_box_noncebytes();
if (data - in >= inlen) {
g_set_error(err, rspamd_keypair_quark(), E2BIG, "invalid size: too small");
@@ -908,7 +788,7 @@ rspamd_keypair_decrypt(struct rspamd_cryptobox_keypair *kp,
if (!rspamd_cryptobox_decrypt_inplace(*out, inlen, nonce, pubkey,
rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL),
- mac, kp->alg)) {
+ mac)) {
g_set_error(err, rspamd_keypair_quark(), EPERM, "verification failed");
g_free(*out);
@@ -942,26 +822,26 @@ rspamd_keypair_encrypt(struct rspamd_cryptobox_keypair *kp,
return FALSE;
}
- local = rspamd_keypair_new(kp->type, kp->alg);
+ local = rspamd_keypair_new(kp->type);
olen = inlen + sizeof(encrypted_magic) +
- rspamd_cryptobox_pk_bytes(kp->alg) +
- rspamd_cryptobox_mac_bytes(kp->alg) +
- rspamd_cryptobox_nonce_bytes(kp->alg);
+ crypto_box_publickeybytes() +
+ crypto_box_macbytes() +
+ crypto_box_noncebytes();
*out = g_malloc(olen);
memcpy(*out, encrypted_magic, sizeof(encrypted_magic));
pubkey = *out + sizeof(encrypted_magic);
- mac = pubkey + rspamd_cryptobox_pk_bytes(kp->alg);
- nonce = mac + rspamd_cryptobox_mac_bytes(kp->alg);
- data = nonce + rspamd_cryptobox_nonce_bytes(kp->alg);
+ mac = pubkey + crypto_box_publickeybytes();
+ nonce = mac + crypto_box_macbytes();
+ data = nonce + crypto_box_noncebytes();
- ottery_rand_bytes(nonce, rspamd_cryptobox_nonce_bytes(kp->alg));
+ ottery_rand_bytes(nonce, crypto_box_noncebytes());
memcpy(data, in, inlen);
- memcpy(pubkey, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, NULL),
- rspamd_cryptobox_pk_bytes(kp->alg));
- rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, pubkey,
+ memcpy(pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_PK, NULL),
+ crypto_box_publickeybytes());
+ rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, NULL),
rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL),
- mac, kp->alg);
+ mac);
rspamd_keypair_unref(local);
if (outlen) {
@@ -991,26 +871,26 @@ rspamd_pubkey_encrypt(struct rspamd_cryptobox_pubkey *pk,
return FALSE;
}
- local = rspamd_keypair_new(pk->type, pk->alg);
+ local = rspamd_keypair_new(pk->type);
olen = inlen + sizeof(encrypted_magic) +
- rspamd_cryptobox_pk_bytes(pk->alg) +
- rspamd_cryptobox_mac_bytes(pk->alg) +
- rspamd_cryptobox_nonce_bytes(pk->alg);
+ crypto_box_publickeybytes() +
+ crypto_box_macbytes() +
+ crypto_box_noncebytes();
*out = g_malloc(olen);
memcpy(*out, encrypted_magic, sizeof(encrypted_magic));
pubkey = *out + sizeof(encrypted_magic);
- mac = pubkey + rspamd_cryptobox_pk_bytes(pk->alg);
- nonce = mac + rspamd_cryptobox_mac_bytes(pk->alg);
- data = nonce + rspamd_cryptobox_nonce_bytes(pk->alg);
+ mac = pubkey + crypto_box_publickeybytes();
+ nonce = mac + crypto_box_macbytes();
+ data = nonce + crypto_box_noncebytes();
- ottery_rand_bytes(nonce, rspamd_cryptobox_nonce_bytes(pk->alg));
+ ottery_rand_bytes(nonce, crypto_box_noncebytes());
memcpy(data, in, inlen);
- memcpy(pubkey, rspamd_pubkey_get_pk(pk, NULL),
- rspamd_cryptobox_pk_bytes(pk->alg));
- rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, pubkey,
+ memcpy(pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_PK, NULL),
+ crypto_box_publickeybytes());
+ rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, rspamd_pubkey_get_pk(pk, NULL),
rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL),
- mac, pk->alg);
+ mac);
rspamd_keypair_unref(local);
if (outlen) {
diff --git a/src/libcryptobox/keypair.h b/src/libcryptobox/keypair.h
index 849246255..97b46cbf5 100644
--- a/src/libcryptobox/keypair.h
+++ b/src/libcryptobox/keypair.h
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -50,8 +50,7 @@ struct rspamd_cryptobox_pubkey;
* @return fresh keypair generated
*/
struct rspamd_cryptobox_keypair *rspamd_keypair_new(
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg);
+ enum rspamd_cryptobox_keypair_type type);
/**
* Increase refcount for the specific keypair
@@ -84,8 +83,7 @@ struct rspamd_cryptobox_pubkey *rspamd_pubkey_ref(
*/
struct rspamd_cryptobox_pubkey *rspamd_pubkey_from_base32(const char *b32,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg);
+ enum rspamd_cryptobox_keypair_type type);
/**
* Load pubkey from hex string
@@ -96,8 +94,7 @@ struct rspamd_cryptobox_pubkey *rspamd_pubkey_from_base32(const char *b32,
*/
struct rspamd_cryptobox_pubkey *rspamd_pubkey_from_hex(const char *hex,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg);
+ enum rspamd_cryptobox_keypair_type type);
/**
* Load pubkey from raw chunk string
@@ -108,8 +105,7 @@ struct rspamd_cryptobox_pubkey *rspamd_pubkey_from_hex(const char *hex,
*/
struct rspamd_cryptobox_pubkey *rspamd_pubkey_from_bin(const unsigned char *raw,
gsize len,
- enum rspamd_cryptobox_keypair_type type,
- enum rspamd_cryptobox_mode alg);
+ enum rspamd_cryptobox_keypair_type type);
/**
@@ -127,18 +123,7 @@ enum rspamd_cryptobox_keypair_type rspamd_keypair_type(
/**
* Get type of pubkey
*/
-enum rspamd_cryptobox_keypair_type rspamd_pubkey_type(
- struct rspamd_cryptobox_pubkey *p);
-
-/**
- * Get algorithm of keypair
- */
-enum rspamd_cryptobox_mode rspamd_keypair_alg(struct rspamd_cryptobox_keypair *kp);
-
-/**
- * Get algorithm of pubkey
- */
-enum rspamd_cryptobox_mode rspamd_pubkey_alg(struct rspamd_cryptobox_pubkey *p);
+enum rspamd_cryptobox_keypair_type rspamd_pubkey_type(struct rspamd_cryptobox_pubkey *p);
/**
* Get cached NM for this specific pubkey
diff --git a/src/libcryptobox/keypair_private.h b/src/libcryptobox/keypair_private.h
index 793231701..2e372777c 100644
--- a/src/libcryptobox/keypair_private.h
+++ b/src/libcryptobox/keypair_private.h
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -38,22 +38,11 @@ struct rspamd_cryptobox_nm {
struct rspamd_cryptobox_keypair {
unsigned char id[rspamd_cryptobox_HASHBYTES];
enum rspamd_cryptobox_keypair_type type;
- enum rspamd_cryptobox_mode alg;
ucl_object_t *extensions;
ref_entry_t ref;
};
/*
- * NIST p256 ecdh keypair
- */
-#define RSPAMD_CRYPTOBOX_KEYPAIR_NIST(x) ((struct rspamd_cryptobox_keypair_nist *) (x))
-struct rspamd_cryptobox_keypair_nist {
- struct rspamd_cryptobox_keypair parent;
- unsigned char sk[32];
- unsigned char pk[65];
-};
-
-/*
* Curve25519 ecdh keypair
*/
#define RSPAMD_CRYPTOBOX_KEYPAIR_25519(x) ((struct rspamd_cryptobox_keypair_25519 *) (x))
@@ -64,16 +53,6 @@ struct rspamd_cryptobox_keypair_25519 {
};
/*
- * NIST p256 ecdsa keypair
- */
-#define RSPAMD_CRYPTOBOX_KEYPAIR_SIG_NIST(x) ((struct rspamd_cryptobox_keypair_sig_nist *) (x))
-struct rspamd_cryptobox_keypair_sig_nist {
- struct rspamd_cryptobox_keypair parent;
- unsigned char sk[32];
- unsigned char pk[65];
-};
-
-/*
* Ed25519 keypair
*/
#define RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(x) ((struct rspamd_cryptobox_keypair_sig_25519 *) (x))
@@ -90,20 +69,10 @@ struct rspamd_cryptobox_pubkey {
unsigned char id[rspamd_cryptobox_HASHBYTES];
struct rspamd_cryptobox_nm *nm;
enum rspamd_cryptobox_keypair_type type;
- enum rspamd_cryptobox_mode alg;
ref_entry_t ref;
};
/*
- * Public p256 ecdh
- */
-#define RSPAMD_CRYPTOBOX_PUBKEY_NIST(x) ((struct rspamd_cryptobox_pubkey_nist *) (x))
-struct rspamd_cryptobox_pubkey_nist {
- struct rspamd_cryptobox_pubkey parent;
- unsigned char pk[65];
-};
-
-/*
* Public curve25519 ecdh
*/
#define RSPAMD_CRYPTOBOX_PUBKEY_25519(x) ((struct rspamd_cryptobox_pubkey_25519 *) (x))
@@ -113,15 +82,6 @@ struct rspamd_cryptobox_pubkey_25519 {
};
/*
- * Public p256 ecdsa
- */
-#define RSPAMD_CRYPTOBOX_PUBKEY_SIG_NIST(x) ((struct rspamd_cryptobox_pubkey_sig_nist *) (x))
-struct rspamd_cryptobox_pubkey_sig_nist {
- struct rspamd_cryptobox_pubkey parent;
- unsigned char pk[65];
-};
-
-/*
* Public ed25519
*/
#define RSPAMD_CRYPTOBOX_PUBKEY_SIG_25519(x) ((struct rspamd_cryptobox_pubkey_sig_25519 *) (x))
diff --git a/src/libcryptobox/keypairs_cache.c b/src/libcryptobox/keypairs_cache.c
index 6003d9923..0b069a64b 100644
--- a/src/libcryptobox/keypairs_cache.c
+++ b/src/libcryptobox/keypairs_cache.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -77,7 +77,6 @@ void rspamd_keypair_cache_process(struct rspamd_keypair_cache *c,
g_assert(lk != NULL);
g_assert(rk != NULL);
- g_assert(rk->alg == lk->alg);
g_assert(rk->type == lk->type);
g_assert(rk->type == RSPAMD_KEYPAIR_KEX);
@@ -106,22 +105,12 @@ void rspamd_keypair_cache_process(struct rspamd_keypair_cache *c,
rspamd_cryptobox_HASHBYTES);
memcpy(&new->nm->sk_id, lk->id, sizeof(uint64_t));
- if (rk->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- struct rspamd_cryptobox_pubkey_25519 *rk_25519 =
- RSPAMD_CRYPTOBOX_PUBKEY_25519(rk);
- struct rspamd_cryptobox_keypair_25519 *sk_25519 =
- RSPAMD_CRYPTOBOX_KEYPAIR_25519(lk);
+ struct rspamd_cryptobox_pubkey_25519 *rk_25519 =
+ RSPAMD_CRYPTOBOX_PUBKEY_25519(rk);
+ struct rspamd_cryptobox_keypair_25519 *sk_25519 =
+ RSPAMD_CRYPTOBOX_KEYPAIR_25519(lk);
- rspamd_cryptobox_nm(new->nm->nm, rk_25519->pk, sk_25519->sk, rk->alg);
- }
- else {
- struct rspamd_cryptobox_pubkey_nist *rk_nist =
- RSPAMD_CRYPTOBOX_PUBKEY_NIST(rk);
- struct rspamd_cryptobox_keypair_nist *sk_nist =
- RSPAMD_CRYPTOBOX_KEYPAIR_NIST(lk);
-
- rspamd_cryptobox_nm(new->nm->nm, rk_nist->pk, sk_nist->sk, rk->alg);
- }
+ rspamd_cryptobox_nm(new->nm->nm, rk_25519->pk, sk_25519->sk);
rspamd_lru_hash_insert(c->hash, new, new, time(NULL), -1);
}
diff --git a/src/libmime/lang_detection.c b/src/libmime/lang_detection.c
index 4796e4834..6e180ea66 100644
--- a/src/libmime/lang_detection.c
+++ b/src/libmime/lang_detection.c
@@ -1828,7 +1828,7 @@ rspamd_language_detector_detect(struct rspamd_task *task,
unsigned int cand_len;
enum rspamd_language_category cat;
struct rspamd_lang_detector_res *cand;
- enum rspamd_language_detected_type r;
+ enum rspamd_language_detected_type r = rs_detect_none;
struct rspamd_frequency_sort_cbdata cbd;
/* Check if we have sorted candidates based on frequency */
gboolean frequency_heuristic_applied = FALSE, ret = FALSE, internal_heuristic_applied = FALSE;
diff --git a/src/libmime/scan_result.c b/src/libmime/scan_result.c
index f15290b95..894ae4f9e 100644
--- a/src/libmime/scan_result.c
+++ b/src/libmime/scan_result.c
@@ -201,16 +201,34 @@ rspamd_check_group_score(struct rspamd_task *task,
double *group_score,
double w)
{
- if (gr != NULL && group_score && gr->max_score > 0.0 && w > 0.0) {
- if (*group_score >= gr->max_score && w > 0) {
+ double group_limit = NAN;
+
+ if (gr != NULL && group_score) {
+ if ((*group_score + w) >= 0 && !isnan(gr->max_score) && gr->max_score > 0) {
+ group_limit = gr->max_score;
+ }
+ else if ((*group_score + w) < 0 && !isnan(gr->min_score) && gr->min_score < 0) {
+ group_limit = -gr->min_score;
+ }
+ }
+
+ if (gr != NULL && group_limit && !isnan(group_limit)) {
+ if (fabs(*group_score) >= group_limit && signbit(*group_score) == signbit(w)) {
+ /* Cannot add more to the group */
msg_info_task("maximum group score %.2f for group %s has been reached,"
" ignoring symbol %s with weight %.2f",
- gr->max_score,
+ group_limit,
gr->name, symbol, w);
return NAN;
}
- else if (*group_score + w > gr->max_score) {
- w = gr->max_score - *group_score;
+ else if (fabs(*group_score + w) > group_limit) {
+ /* Reduce weight */
+ double new_w = signbit(w) ? -group_limit - *group_score : group_limit - *group_score;
+ msg_info_task("maximum group score %.2f for group %s has been reached,"
+ " reduce weight of symbol %s from %.2f to %.2f",
+ group_limit,
+ gr->name, symbol, w, new_w);
+ w = new_w;
}
}
@@ -393,15 +411,7 @@ insert_metric_result(struct rspamd_task *task,
}
else if (gr_score) {
*gr_score += cur_diff;
-
- if (cur_diff < diff) {
- /* Reduce */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " reduce score %.2f - %.2f",
- *gr_score, gr->name, symbol, diff, cur_diff);
- diff = cur_diff;
- }
+ diff = cur_diff;
}
}
}
@@ -461,15 +471,7 @@ insert_metric_result(struct rspamd_task *task,
}
else if (gr_score) {
*gr_score += cur_score;
-
- if (cur_score < final_score) {
- /* Reduce */
- msg_debug_metric(
- "group limit %.2f is reached for %s when inserting symbol %s;"
- " reduce score %.2f - %.2f",
- *gr_score, gr->name, symbol, final_score, cur_score);
- final_score = cur_score;
- }
+ final_score = cur_score;
}
}
}
diff --git a/src/libserver/cfg_file.h b/src/libserver/cfg_file.h
index a963f952f..f59c6ff89 100644
--- a/src/libserver/cfg_file.h
+++ b/src/libserver/cfg_file.h
@@ -102,6 +102,7 @@ struct rspamd_symbols_group {
char *description;
GHashTable *symbols;
double max_score;
+ double min_score;
unsigned int flags;
};
diff --git a/src/libserver/cfg_rcl.cxx b/src/libserver/cfg_rcl.cxx
index 2fe37f18e..79509e12e 100644
--- a/src/libserver/cfg_rcl.cxx
+++ b/src/libserver/cfg_rcl.cxx
@@ -420,6 +420,18 @@ rspamd_rcl_group_handler(rspamd_mempool_t *pool, const ucl_object_t *obj,
return FALSE;
}
+ if (!std::isnan(gr->max_score) && gr->max_score < 0) {
+ msg_err_config("group %s has negative max_score which is broken, use min_score if required", gr->name);
+
+ return FALSE;
+ }
+ if (!std::isnan(gr->min_score) && gr->min_score > 0) {
+ msg_err_config("group %s has positive min_score which is broken, use max_score if required", gr->name);
+
+ return FALSE;
+ }
+
+
if (const auto *elt = ucl_object_lookup(obj, "one_shot"); elt != nullptr) {
if (ucl_object_type(elt) != UCL_BOOLEAN) {
g_set_error(err,
@@ -2355,6 +2367,12 @@ rspamd_rcl_config_init(struct rspamd_config *cfg, GHashTable *skip_sections)
G_STRUCT_OFFSET(struct rspamd_symbols_group, max_score),
0,
"Maximum score that could be reached by this symbols group");
+ rspamd_rcl_add_default_handler(sub,
+ "min_score",
+ rspamd_rcl_parse_struct_double,
+ G_STRUCT_OFFSET(struct rspamd_symbols_group, min_score),
+ 0,
+ "Maximum negative score that could be reached by this symbols group");
}
if (!(skip_sections && g_hash_table_lookup(skip_sections, "worker"))) {
@@ -3039,21 +3057,16 @@ rspamd_rcl_parse_struct_pubkey(rspamd_mempool_t *pool,
gsize len;
const char *str;
rspamd_cryptobox_keypair_type keypair_type = RSPAMD_KEYPAIR_KEX;
- rspamd_cryptobox_mode keypair_mode = RSPAMD_CRYPTOBOX_MODE_25519;
if (pd->flags & RSPAMD_CL_FLAG_SIGNKEY) {
keypair_type = RSPAMD_KEYPAIR_SIGN;
}
- if (pd->flags & RSPAMD_CL_FLAG_NISTKEY) {
- keypair_mode = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
target = (struct rspamd_cryptobox_pubkey **) (((char *) pd->user_struct) +
pd->offset);
if (obj->type == UCL_STRING) {
str = ucl_object_tolstring(obj, &len);
- pk = rspamd_pubkey_from_base32(str, len, keypair_type,
- keypair_mode);
+ pk = rspamd_pubkey_from_base32(str, len, keypair_type);
if (pk != nullptr) {
*target = pk;
@@ -3482,7 +3495,7 @@ void rspamd_rcl_maybe_apply_lua_transform(struct rspamd_config *cfg)
lua_pushvalue(L, -2);
/* Push the existing config */
- ucl_object_push_lua(L, cfg->cfg_ucl_obj, true);
+ ucl_object_push_lua_unwrapped(L, cfg->cfg_ucl_obj);
if (auto ret = lua_pcall(L, 1, 2, err_idx); ret != 0) {
msg_err("call to rspamadm lua script failed (%d): %s", ret,
@@ -3492,12 +3505,8 @@ void rspamd_rcl_maybe_apply_lua_transform(struct rspamd_config *cfg)
return;
}
- if (lua_toboolean(L, -2) && lua_type(L, -1) == LUA_TTABLE) {
- ucl_object_t *old_cfg = cfg->cfg_ucl_obj;
-
+ if (lua_toboolean(L, -2) && lua_type(L, -1) == LUA_TUSERDATA) {
msg_info_config("configuration has been transformed in Lua");
- cfg->cfg_ucl_obj = ucl_object_lua_import(L, -1);
- ucl_object_unref(old_cfg);
}
/* error function */
@@ -3629,7 +3638,8 @@ rspamd_config_parse_ucl(struct rspamd_config *cfg,
auto &cfg_file = cfg_file_maybe.value();
/* Try to load keyfile if available */
- rspamd::util::raii_file::open(fmt::format("{}.key", filename), O_RDONLY).map([&](const auto &keyfile) {
+ auto keyfile_name = fmt::format("{}.key", filename);
+ rspamd::util::raii_file::open(keyfile_name, O_RDONLY).map([&](const auto &keyfile) {
auto *kp_parser = ucl_parser_new(0);
if (ucl_parser_add_fd(kp_parser, keyfile.get_fd())) {
auto *kp_obj = ucl_parser_get_object(kp_parser);
@@ -3638,8 +3648,8 @@ rspamd_config_parse_ucl(struct rspamd_config *cfg,
decrypt_keypair = rspamd_keypair_from_ucl(kp_obj);
if (decrypt_keypair == nullptr) {
- msg_err_config_forced("cannot load keypair from %s.key: invalid keypair",
- filename);
+ msg_err_config_forced("cannot load keypair from %s: invalid keypair",
+ keyfile_name.c_str());
}
else {
/* Add decryption support to UCL */
@@ -3651,8 +3661,8 @@ rspamd_config_parse_ucl(struct rspamd_config *cfg,
ucl_object_unref(kp_obj);
}
else {
- msg_err_config_forced("cannot load keypair from %s.key: %s",
- filename, ucl_parser_get_error(kp_parser));
+ msg_err_config_forced("cannot load keypair from %s: %s",
+ keyfile_name.c_str(), ucl_parser_get_error(kp_parser));
}
ucl_parser_free(kp_parser);
});
diff --git a/src/libserver/cfg_rcl.h b/src/libserver/cfg_rcl.h
index e33656b72..35b9b931f 100644
--- a/src/libserver/cfg_rcl.h
+++ b/src/libserver/cfg_rcl.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Vsevolod Stakhov
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,7 +53,6 @@ enum rspamd_rcl_flag {
RSPAMD_CL_FLAG_STRING_LIST_HASH = 0x1 << 12,
RSPAMD_CL_FLAG_MULTIPLE = 0x1 << 13,
RSPAMD_CL_FLAG_SIGNKEY = 0x1 << 14,
- RSPAMD_CL_FLAG_NISTKEY = 0x1 << 15,
};
struct rspamd_rcl_struct_parser {
diff --git a/src/libserver/cfg_utils.cxx b/src/libserver/cfg_utils.cxx
index f17caa8a5..38adf8390 100644
--- a/src/libserver/cfg_utils.cxx
+++ b/src/libserver/cfg_utils.cxx
@@ -1053,6 +1053,8 @@ rspamd_config_new_group(struct rspamd_config *cfg, const char *name)
rspamd_mempool_add_destructor(cfg->cfg_pool,
(rspamd_mempool_destruct_t) g_hash_table_unref, gr->symbols);
gr->name = rspamd_mempool_strdup(cfg->cfg_pool, name);
+ gr->max_score = NAN;
+ gr->min_score = NAN;
if (strcmp(gr->name, "ungrouped") == 0) {
gr->flags |= RSPAMD_SYMBOL_GROUP_UNGROUPED;
diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c
index 742e4db8b..a76ed31ab 100644
--- a/src/libserver/dkim.c
+++ b/src/libserver/dkim.c
@@ -155,12 +155,12 @@ struct rspamd_dkim_key_s {
gsize decoded_len;
char key_id[RSPAMD_DKIM_KEY_ID_LEN];
union {
- RSA *key_rsa;
- EC_KEY *key_ecdsa;
unsigned char *key_eddsa;
- } key;
- BIO *key_bio;
- EVP_PKEY *key_evp;
+ struct {
+ BIO *key_bio;
+ EVP_PKEY *key_evp;
+ } key_ssl;
+ } specific;
time_t mtime;
unsigned int ttl;
enum rspamd_dkim_key_type type;
@@ -790,12 +790,12 @@ rspamd_dkim_add_arc_seal_headers(rspamd_mempool_t *pool,
}
/**
- * Create new dkim context from signature
- * @param sig message's signature
- * @param pool pool to allocate memory from
- * @param err pointer to error object
- * @return new context or NULL
- */
+* Create new dkim context from signature
+* @param sig message's signature
+* @param pool pool to allocate memory from
+* @param err pointer to error object
+* @return new context or NULL
+*/
rspamd_dkim_context_t *
rspamd_create_dkim_context(const char *sig,
rspamd_mempool_t *pool,
@@ -1097,9 +1097,9 @@ rspamd_create_dkim_context(const char *sig,
if (state == DKIM_STATE_ERROR) {
/*
- * We need to return from here as state machine won't
- * do any more steps after p == end
- */
+ * We need to return from here as state machine won't
+ * do any more steps after p == end
+ */
if (err) {
msg_info_dkim("dkim parse failed: %e", *err);
}
@@ -1391,25 +1391,24 @@ rspamd_dkim_make_key(const char *keydata,
EVP_MD_CTX_destroy(mdctx);
if (key->type == RSPAMD_DKIM_KEY_EDDSA) {
- key->key.key_eddsa = key->keydata;
+ key->specific.key_eddsa = key->keydata;
- if (key->decoded_len != rspamd_cryptobox_pk_sig_bytes(
- RSPAMD_CRYPTOBOX_MODE_25519)) {
+ if (key->decoded_len != crypto_sign_publickeybytes()) {
g_set_error(err,
DKIM_ERROR,
DKIM_SIGERROR_KEYFAIL,
- "DKIM key is has invalid length %d for eddsa; expected %d",
+ "DKIM key is has invalid length %d for eddsa; expected %zd",
(int) key->decoded_len,
- rspamd_cryptobox_pk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519));
+ crypto_sign_publickeybytes());
REF_RELEASE(key);
return NULL;
}
}
else {
- key->key_bio = BIO_new_mem_buf(key->keydata, key->decoded_len);
+ key->specific.key_ssl.key_bio = BIO_new_mem_buf(key->keydata, key->decoded_len);
- if (key->key_bio == NULL) {
+ if (key->specific.key_ssl.key_bio == NULL) {
g_set_error(err,
DKIM_ERROR,
DKIM_SIGERROR_KEYFAIL,
@@ -1419,9 +1418,9 @@ rspamd_dkim_make_key(const char *keydata,
return NULL;
}
- key->key_evp = d2i_PUBKEY_bio(key->key_bio, NULL);
+ key->specific.key_ssl.key_evp = d2i_PUBKEY_bio(key->specific.key_ssl.key_bio, NULL);
- if (key->key_evp == NULL) {
+ if (key->specific.key_ssl.key_evp == NULL) {
g_set_error(err,
DKIM_ERROR,
DKIM_SIGERROR_KEYFAIL,
@@ -1430,33 +1429,6 @@ rspamd_dkim_make_key(const char *keydata,
return NULL;
}
-
- if (type == RSPAMD_DKIM_KEY_RSA) {
- key->key.key_rsa = EVP_PKEY_get1_RSA(key->key_evp);
-
- if (key->key.key_rsa == NULL) {
- g_set_error(err,
- DKIM_ERROR,
- DKIM_SIGERROR_KEYFAIL,
- "cannot extract rsa key from evp key");
- REF_RELEASE(key);
-
- return NULL;
- }
- }
- else {
- key->key.key_ecdsa = EVP_PKEY_get1_EC_KEY(key->key_evp);
-
- if (key->key.key_ecdsa == NULL) {
- g_set_error(err,
- DKIM_ERROR,
- DKIM_SIGERROR_KEYFAIL,
- "cannot extract ecdsa key from evp key");
- REF_RELEASE(key);
-
- return NULL;
- }
- }
}
return key;
@@ -1473,29 +1445,19 @@ rspamd_dkim_key_id(rspamd_dkim_key_t *key)
}
/**
- * Free DKIM key
- * @param key
- */
+* Free DKIM key
+* @param key
+*/
void rspamd_dkim_key_free(rspamd_dkim_key_t *key)
{
- if (key->key_evp) {
- EVP_PKEY_free(key->key_evp);
- }
-
- if (key->type == RSPAMD_DKIM_KEY_RSA) {
- if (key->key.key_rsa) {
- RSA_free(key->key.key_rsa);
+ if (key->type != RSPAMD_DKIM_KEY_EDDSA) {
+ if (key->specific.key_ssl.key_evp) {
+ EVP_PKEY_free(key->specific.key_ssl.key_evp);
}
- }
- else if (key->type == RSPAMD_DKIM_KEY_ECDSA) {
- if (key->key.key_ecdsa) {
- EC_KEY_free(key->key.key_ecdsa);
+ if (key->specific.key_ssl.key_bio) {
+ BIO_free(key->specific.key_ssl.key_bio);
}
}
- /* Nothing in case of eddsa key */
- if (key->key_bio) {
- BIO_free(key->key_bio);
- }
g_free(key->raw_key);
g_free(key->keydata);
@@ -1504,20 +1466,16 @@ void rspamd_dkim_key_free(rspamd_dkim_key_t *key)
void rspamd_dkim_sign_key_free(rspamd_dkim_sign_key_t *key)
{
- if (key->key_evp) {
- EVP_PKEY_free(key->key_evp);
- }
- if (key->type == RSPAMD_DKIM_KEY_RSA) {
- if (key->key.key_rsa) {
- RSA_free(key->key.key_rsa);
+ if (key->type != RSPAMD_DKIM_KEY_EDDSA) {
+ if (key->specific.key_ssl.key_evp) {
+ EVP_PKEY_free(key->specific.key_ssl.key_evp);
+ }
+ if (key->specific.key_ssl.key_bio) {
+ BIO_free(key->specific.key_ssl.key_bio);
}
}
- if (key->key_bio) {
- BIO_free(key->key_bio);
- }
-
- if (key->type == RSPAMD_DKIM_KEY_EDDSA) {
- rspamd_explicit_memzero(key->key.key_eddsa, key->keylen);
+ else {
+ rspamd_explicit_memzero(key->specific.key_eddsa, key->keylen);
g_free(key->keydata);
}
@@ -1570,9 +1528,9 @@ rspamd_dkim_parse_key(const char *txt, gsize *keylen, GError **err)
break;
case read_tag_before_eqsign:
/* Input: spaces before eqsign
- * Output: either read a next tag (previous had no value), or read value
- * p is moved forward
- */
+ * Output: either read a next tag (previous had no value), or read value
+ * p is moved forward
+ */
if (*p == '=') {
state = read_eqsign;
}
@@ -1759,12 +1717,12 @@ rspamd_dkim_dns_cb(struct rdns_reply *reply, gpointer arg)
}
/**
- * Make DNS request for specified context and obtain and parse key
- * @param ctx dkim context from signature
- * @param resolver dns resolver object
- * @param s async session to make request
- * @return
- */
+* Make DNS request for specified context and obtain and parse key
+* @param ctx dkim context from signature
+* @param resolver dns resolver object
+* @param s async session to make request
+* @return
+*/
gboolean
rspamd_get_dkim_key(rspamd_dkim_context_t *ctx,
struct rspamd_task *task,
@@ -2187,8 +2145,8 @@ rspamd_dkim_canonize_body(struct rspamd_task *task,
;
/*
- * If we have l= tag then we cannot add crlf...
- */
+ * If we have l= tag then we cannot add crlf...
+ */
if (need_crlf) {
/* l is evil... */
if (ctx->len == 0) {
@@ -2241,7 +2199,6 @@ rspamd_dkim_canonize_body(struct rspamd_task *task,
return TRUE;
}
- /* TODO: Implement relaxed algorithm */
return FALSE;
}
@@ -2461,9 +2418,9 @@ rspamd_dkim_canonize_header(struct rspamd_dkim_common_ctx *ctx,
bool use_idx = false, is_sign = ctx->is_sign;
/*
- * TODO:
- * Temporary hack to prevent linked list being misused until refactored
- */
+ * TODO:
+ * Temporary hack to prevent linked list being misused until refactored
+ */
const unsigned int max_list_iters = 1000;
if (count < 0) {
@@ -2509,17 +2466,17 @@ rspamd_dkim_canonize_header(struct rspamd_dkim_common_ctx *ctx,
if (hdr_cnt <= count) {
/*
- * If DKIM has less headers requested than there are in a
- * message, then it's fine, it allows adding extra headers
- */
+ * If DKIM has less headers requested than there are in a
+ * message, then it's fine, it allows adding extra headers
+ */
return TRUE;
}
}
else {
/*
- * This branch is used for ARC headers, and it orders them based on
- * i=<number> string and not their real order in the list of headers
- */
+ * This branch is used for ARC headers, and it orders them based on
+ * i=<number> string and not their real order in the list of headers
+ */
char idx_buf[16];
int id_len, i;
@@ -2692,12 +2649,12 @@ rspamd_dkim_type_to_string(enum rspamd_dkim_type t)
}
/**
- * Check task for dkim context using dkim key
- * @param ctx dkim verify context
- * @param key dkim key (from cache or from dns request)
- * @param task task to check
- * @return
- */
+* Check task for dkim context using dkim key
+* @param ctx dkim verify context
+* @param key dkim key (from cache or from dns request)
+* @param task task to check
+* @return
+*/
struct rspamd_dkim_check_result *
rspamd_dkim_check(rspamd_dkim_context_t *ctx,
rspamd_dkim_key_t *key,
@@ -2913,11 +2870,10 @@ rspamd_dkim_check(rspamd_dkim_context_t *ctx,
/* Not reached */
nid = NID_sha1;
}
-
switch (key->type) {
case RSPAMD_DKIM_KEY_RSA:
- if (RSA_verify(nid, raw_digest, dlen, ctx->b, ctx->blen,
- key->key.key_rsa) != 1) {
+ if (!rspamd_cryptobox_verify_evp_rsa(nid, ctx->b, ctx->blen, raw_digest, dlen,
+ key->specific.key_ssl.key_evp)) {
msg_debug_dkim("headers rsa verify failed");
ERR_clear_error();
res->rcode = DKIM_REJECT;
@@ -2935,8 +2891,8 @@ rspamd_dkim_check(rspamd_dkim_context_t *ctx,
}
break;
case RSPAMD_DKIM_KEY_ECDSA:
- if (ECDSA_verify(nid, raw_digest, dlen, ctx->b, ctx->blen,
- key->key.key_ecdsa) != 1) {
+ if (rspamd_cryptobox_verify_evp_ecdsa(nid, ctx->b, ctx->blen, raw_digest, dlen,
+ key->specific.key_ssl.key_evp) != 1) {
msg_info_dkim(
"%s: headers ECDSA verification failure; "
"body length %d->%d; headers length %d; d=%s; s=%s; key_md5=%*xs; orig header: %s",
@@ -2952,9 +2908,10 @@ rspamd_dkim_check(rspamd_dkim_context_t *ctx,
res->fail_reason = "headers ecdsa verify failed";
}
break;
+
case RSPAMD_DKIM_KEY_EDDSA:
if (!rspamd_cryptobox_verify(ctx->b, ctx->blen, raw_digest, dlen,
- key->key.key_eddsa, RSPAMD_CRYPTOBOX_MODE_25519)) {
+ key->specific.key_eddsa)) {
msg_info_dkim(
"%s: headers EDDSA verification failure; "
"body length %d->%d; headers length %d; d=%s; s=%s; key_md5=%*xs; orig header: %s",
@@ -3155,30 +3112,29 @@ rspamd_dkim_sign_key_load(const char *key, gsize len,
}
if (type == RSPAMD_DKIM_KEY_RAW && (len == 32 ||
- len == rspamd_cryptobox_sk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519))) {
+ len == crypto_sign_secretkeybytes())) {
if (len == 32) {
/* Seeded key, need scalarmult */
unsigned char pk[32];
nkey->type = RSPAMD_DKIM_KEY_EDDSA;
- nkey->key.key_eddsa = g_malloc(
- rspamd_cryptobox_sk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519));
- crypto_sign_ed25519_seed_keypair(pk, nkey->key.key_eddsa, key);
- nkey->keylen = rspamd_cryptobox_sk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519);
+ nkey->specific.key_eddsa = g_malloc(crypto_sign_secretkeybytes());
+ crypto_sign_ed25519_seed_keypair(pk, nkey->specific.key_eddsa, key);
+ nkey->keylen = crypto_sign_secretkeybytes();
}
else {
/* Full ed25519 key */
- unsigned klen = rspamd_cryptobox_sk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519);
+ unsigned klen = crypto_sign_secretkeybytes();
nkey->type = RSPAMD_DKIM_KEY_EDDSA;
- nkey->key.key_eddsa = g_malloc(klen);
- memcpy(nkey->key.key_eddsa, key, klen);
+ nkey->specific.key_eddsa = g_malloc(klen);
+ memcpy(nkey->specific.key_eddsa, key, klen);
nkey->keylen = klen;
}
}
else {
- nkey->key_bio = BIO_new_mem_buf(key, len);
+ nkey->specific.key_ssl.key_bio = BIO_new_mem_buf(key, len);
if (type == RSPAMD_DKIM_KEY_RAW) {
- if (d2i_PrivateKey_bio(nkey->key_bio, &nkey->key_evp) == NULL) {
+ if (d2i_PrivateKey_bio(nkey->specific.key_ssl.key_bio, &nkey->specific.key_ssl.key_evp) == NULL) {
g_set_error(err, dkim_error_quark(), DKIM_SIGERROR_KEYFAIL,
"cannot parse raw private key: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -3190,7 +3146,7 @@ rspamd_dkim_sign_key_load(const char *key, gsize len,
}
}
else {
- if (!PEM_read_bio_PrivateKey(nkey->key_bio, &nkey->key_evp, NULL, NULL)) {
+ if (!PEM_read_bio_PrivateKey(nkey->specific.key_ssl.key_bio, &nkey->specific.key_ssl.key_evp, NULL, NULL)) {
g_set_error(err, dkim_error_quark(), DKIM_SIGERROR_KEYFAIL,
"cannot parse pem private key: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -3200,18 +3156,6 @@ rspamd_dkim_sign_key_load(const char *key, gsize len,
goto end;
}
}
- nkey->key.key_rsa = EVP_PKEY_get1_RSA(nkey->key_evp);
- if (nkey->key.key_rsa == NULL) {
- g_set_error(err,
- DKIM_ERROR,
- DKIM_SIGERROR_KEYFAIL,
- "cannot extract rsa key from evp key");
- rspamd_dkim_sign_key_free(nkey);
- nkey = NULL;
-
- goto end;
- }
- nkey->type = RSPAMD_DKIM_KEY_RSA;
}
REF_INIT_RETAIN(nkey, rspamd_dkim_sign_key_free);
@@ -3269,7 +3213,7 @@ rspamd_create_dkim_sign_context(struct rspamd_task *task,
return NULL;
}
- if (!priv_key || (!priv_key->key.key_rsa && !priv_key->key.key_eddsa)) {
+ if (!priv_key) {
g_set_error(err,
DKIM_ERROR,
DKIM_SIGERROR_KEYFAIL,
@@ -3536,12 +3480,34 @@ rspamd_dkim_sign(struct rspamd_task *task, const char *selector,
dlen = EVP_MD_CTX_size(ctx->common.headers_hash);
EVP_DigestFinal_ex(ctx->common.headers_hash, raw_digest, NULL);
+
if (ctx->key->type == RSPAMD_DKIM_KEY_RSA) {
- sig_len = RSA_size(ctx->key->key.key_rsa);
+ sig_len = EVP_PKEY_size(ctx->key->specific.key_ssl.key_evp);
sig_buf = g_alloca(sig_len);
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ctx->key->specific.key_ssl.key_evp, NULL);
+ if (EVP_PKEY_sign_init(pctx) <= 0) {
+ g_string_free(hdr, TRUE);
+ msg_err_task("rsa sign error: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+
+ return NULL;
+ }
+ if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0) {
+ g_string_free(hdr, TRUE);
+ msg_err_task("rsa sign error: %s",
+ ERR_error_string(ERR_get_error(), NULL));
- if (RSA_sign(NID_sha256, raw_digest, dlen, sig_buf, &sig_len,
- ctx->key->key.key_rsa) != 1) {
+ return NULL;
+ }
+ if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha256()) <= 0) {
+ g_string_free(hdr, TRUE);
+ msg_err_task("rsa sign error: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+
+ return NULL;
+ }
+ size_t sig_len_size_t = sig_len;
+ if (EVP_PKEY_sign(pctx, sig_buf, &sig_len_size_t, raw_digest, dlen) <= 0) {
g_string_free(hdr, TRUE);
msg_err_task("rsa sign error: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -3550,11 +3516,10 @@ rspamd_dkim_sign(struct rspamd_task *task, const char *selector,
}
}
else if (ctx->key->type == RSPAMD_DKIM_KEY_EDDSA) {
- sig_len = rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519);
+ sig_len = crypto_sign_bytes();
sig_buf = g_alloca(sig_len);
- rspamd_cryptobox_sign(sig_buf, NULL, raw_digest, dlen,
- ctx->key->key.key_eddsa, RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_sign(sig_buf, NULL, raw_digest, dlen, ctx->key->specific.key_eddsa);
}
else {
g_string_free(hdr, TRUE);
@@ -3595,17 +3560,25 @@ rspamd_dkim_match_keys(rspamd_dkim_key_t *pk,
}
if (pk->type == RSPAMD_DKIM_KEY_EDDSA) {
- if (memcmp(sk->key.key_eddsa + 32, pk->key.key_eddsa, 32) != 0) {
+ if (memcmp(sk->specific.key_eddsa + 32, pk->specific.key_eddsa, 32) != 0) {
g_set_error(err, dkim_error_quark(), DKIM_SIGERROR_KEYHASHMISMATCH,
"pubkey does not match private key");
return FALSE;
}
}
- else if (EVP_PKEY_cmp(pk->key_evp, sk->key_evp) != 1) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ else if (EVP_PKEY_eq(pk->specific.key_ssl.key_evp, sk->specific.key_ssl.key_evp) != 1) {
g_set_error(err, dkim_error_quark(), DKIM_SIGERROR_KEYHASHMISMATCH,
"pubkey does not match private key");
return FALSE;
}
+#else
+ else if (EVP_PKEY_cmp(pk->specific.key_ssl.key_evp, sk->specific.key_ssl.key_evp) != 1) {
+ g_set_error(err, dkim_error_quark(), DKIM_SIGERROR_KEYHASHMISMATCH,
+ "pubkey does not match private key");
+ return FALSE;
+ }
+#endif
return TRUE;
}
diff --git a/src/libserver/http/http_connection.c b/src/libserver/http/http_connection.c
index ef39e11e7..1ae9bb034 100644
--- a/src/libserver/http/http_connection.c
+++ b/src/libserver/http/http_connection.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -159,8 +159,7 @@ rspamd_http_parse_key(rspamd_ftok_t *data, struct rspamd_http_connection *conn,
if (decoded_id != NULL && id_len >= RSPAMD_KEYPAIR_SHORT_ID_LEN) {
pk = rspamd_pubkey_from_base32(eq_pos + 1,
data->begin + data->len - eq_pos - 1,
- RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ RSPAMD_KEYPAIR_KEX);
if (pk != NULL) {
if (memcmp(rspamd_keypair_get_id(priv->local_key),
decoded_id,
@@ -572,21 +571,18 @@ rspamd_http_decrypt_message(struct rspamd_http_connection *conn,
struct rspamd_http_header *hdr, *hcur, *hcurtmp;
struct http_parser decrypted_parser;
struct http_parser_settings decrypted_cb;
- enum rspamd_cryptobox_mode mode;
- mode = rspamd_keypair_alg(priv->local_key);
nonce = msg->body_buf.str;
- m = msg->body_buf.str + rspamd_cryptobox_nonce_bytes(mode) +
- rspamd_cryptobox_mac_bytes(mode);
- dec_len = msg->body_buf.len - rspamd_cryptobox_nonce_bytes(mode) -
- rspamd_cryptobox_mac_bytes(mode);
+ m = msg->body_buf.str + crypto_box_noncebytes() +
+ crypto_box_macbytes();
+ dec_len = msg->body_buf.len - crypto_box_noncebytes() - crypto_box_macbytes();
if ((nm = rspamd_pubkey_get_nm(peer_key, priv->local_key)) == NULL) {
nm = rspamd_pubkey_calculate_nm(peer_key, priv->local_key);
}
if (!rspamd_cryptobox_decrypt_nm_inplace(m, dec_len, nonce,
- nm, m - rspamd_cryptobox_mac_bytes(mode), mode)) {
+ nm, m - crypto_box_macbytes())) {
msg_err("cannot verify encrypted message, first bytes of the input: %*xs",
(int) MIN(msg->body_buf.len, 64), msg->body_buf.begin);
return -1;
@@ -640,7 +636,6 @@ rspamd_http_on_message_complete(http_parser *parser)
(struct rspamd_http_connection *) parser->data;
struct rspamd_http_connection_private *priv;
int ret = 0;
- enum rspamd_cryptobox_mode mode;
if (conn->finished) {
return 0;
@@ -655,11 +650,10 @@ rspamd_http_on_message_complete(http_parser *parser)
}
if ((conn->opts & RSPAMD_HTTP_BODY_PARTIAL) == 0 && IS_CONN_ENCRYPTED(priv)) {
- mode = rspamd_keypair_alg(priv->local_key);
if (priv->local_key == NULL || priv->msg->peer_key == NULL ||
- priv->msg->body_buf.len < rspamd_cryptobox_nonce_bytes(mode) +
- rspamd_cryptobox_mac_bytes(mode)) {
+ priv->msg->body_buf.len < crypto_box_noncebytes() +
+ crypto_box_macbytes()) {
msg_err("cannot decrypt message");
return -1;
}
@@ -1576,10 +1570,8 @@ rspamd_http_connection_encrypt_message(
int i, cnt;
unsigned int outlen;
struct rspamd_http_header *hdr, *hcur;
- enum rspamd_cryptobox_mode mode;
- mode = rspamd_keypair_alg(priv->local_key);
- crlfp = mp + rspamd_cryptobox_mac_bytes(mode);
+ crlfp = mp + crypto_box_macbytes();
outlen = priv->out[0].iov_len + priv->out[1].iov_len;
/*
@@ -1632,7 +1624,7 @@ if ((nm = rspamd_pubkey_get_nm(peer_key, priv->local_key)) == NULL) {
nm = rspamd_pubkey_calculate_nm(peer_key, priv->local_key);
}
-rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, np, nm, mp, mode);
+rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, np, nm, mp);
/*
* iov[0] = base HTTP request
@@ -1642,12 +1634,12 @@ rspamd_cryptobox_encryptv_nm_inplace(segments, cnt, np, nm, mp, mode);
* iov[4..i] = encrypted HTTP request/reply
*/
priv->out[2].iov_base = np;
-priv->out[2].iov_len = rspamd_cryptobox_nonce_bytes(mode);
+priv->out[2].iov_len = crypto_box_noncebytes();
priv->out[3].iov_base = mp;
-priv->out[3].iov_len = rspamd_cryptobox_mac_bytes(mode);
+priv->out[3].iov_len = crypto_box_macbytes();
-outlen += rspamd_cryptobox_nonce_bytes(mode) +
- rspamd_cryptobox_mac_bytes(mode);
+outlen += crypto_box_noncebytes() +
+ crypto_box_macbytes();
for (i = 0; i < cnt; i++) {
priv->out[i + 4].iov_base = segments[i].data;
@@ -2027,7 +2019,6 @@ rspamd_http_connection_write_message_common(struct rspamd_http_connection *conn,
unsigned char nonce[rspamd_cryptobox_MAX_NONCEBYTES], mac[rspamd_cryptobox_MAX_MACBYTES];
unsigned char *np = NULL, *mp = NULL, *meth_pos = NULL;
struct rspamd_cryptobox_pubkey *peer_key = NULL;
- enum rspamd_cryptobox_mode mode;
GError *err;
conn->ud = ud;
@@ -2059,8 +2050,7 @@ rspamd_http_connection_write_message_common(struct rspamd_http_connection *conn,
if (msg->peer_key != NULL) {
if (priv->local_key == NULL) {
/* Automatically generate a temporary keypair */
- priv->local_key = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ priv->local_key = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
}
encrypted = TRUE;
@@ -2128,8 +2118,6 @@ rspamd_http_connection_write_message_common(struct rspamd_http_connection *conn,
}
if (encrypted) {
- mode = rspamd_keypair_alg(priv->local_key);
-
if (msg->body_buf.len == 0) {
pbody = NULL;
bodylen = 0;
@@ -2154,8 +2142,8 @@ rspamd_http_connection_write_message_common(struct rspamd_http_connection *conn,
* [iov[n + 2] = encrypted body]
*/
priv->outlen = 7;
- enclen = rspamd_cryptobox_nonce_bytes(mode) +
- rspamd_cryptobox_mac_bytes(mode) +
+ enclen = crypto_box_noncebytes() +
+ crypto_box_macbytes() +
4 + /* 2 * CRLF */
bodylen;
}
@@ -2197,8 +2185,8 @@ rspamd_http_connection_write_message_common(struct rspamd_http_connection *conn,
ENCRYPTED_VERSION);
}
- enclen = rspamd_cryptobox_nonce_bytes(mode) +
- rspamd_cryptobox_mac_bytes(mode) +
+ enclen = crypto_box_noncebytes() +
+ crypto_box_macbytes() +
preludelen + /* version [content-length] + 2 * CRLF */
bodylen;
}
@@ -2275,10 +2263,9 @@ priv->out[0].iov_len = buf->len;
/* Buf will be used eventually for encryption */
if (encrypted) {
int meth_offset, nonce_offset, mac_offset;
- mode = rspamd_keypair_alg(priv->local_key);
- ottery_rand_bytes(nonce, rspamd_cryptobox_nonce_bytes(mode));
- memset(mac, 0, rspamd_cryptobox_mac_bytes(mode));
+ ottery_rand_bytes(nonce, crypto_box_noncebytes());
+ memset(mac, 0, crypto_box_macbytes());
meth_offset = buf->len;
if (conn->type == RSPAMD_HTTP_SERVER) {
@@ -2292,11 +2279,9 @@ if (encrypted) {
}
nonce_offset = buf->len;
- buf = rspamd_fstring_append(buf, nonce,
- rspamd_cryptobox_nonce_bytes(mode));
+ buf = rspamd_fstring_append(buf, nonce, crypto_box_noncebytes());
mac_offset = buf->len;
- buf = rspamd_fstring_append(buf, mac,
- rspamd_cryptobox_mac_bytes(mode));
+ buf = rspamd_fstring_append(buf, mac, crypto_box_macbytes());
/* Need to be encrypted */
if (conn->type == RSPAMD_HTTP_SERVER) {
@@ -2365,44 +2350,44 @@ if (conn->opts & RSPAMD_HTTP_CLIENT_SSL) {
gpointer ssl_ctx = (msg->flags & RSPAMD_HTTP_FLAG_SSL_NOVERIFY) ? priv->ctx->ssl_ctx_noverify : priv->ctx->ssl_ctx;
if (!ssl_ctx) {
- err = g_error_new(HTTP_ERROR, 400, "ssl message requested "
- "with no ssl ctx");
- rspamd_http_connection_ref(conn);
- conn->error_handler(conn, err);
- rspamd_http_connection_unref(conn);
- g_error_free(err);
- return FALSE;
+ err = g_error_new(HTTP_ERROR, 400, "ssl message requested "
+ "with no ssl ctx");
+ rspamd_http_connection_ref(conn);
+ conn->error_handler(conn, err);
+ rspamd_http_connection_unref(conn);
+ g_error_free(err);
+ return FALSE;
}
else {
- if (!priv->ssl) {
- priv->ssl = rspamd_ssl_connection_new(ssl_ctx, priv->ctx->event_loop,
- !(msg->flags & RSPAMD_HTTP_FLAG_SSL_NOVERIFY),
- conn->log_tag);
- g_assert(priv->ssl != NULL);
-
- if (!rspamd_ssl_connect_fd(priv->ssl, conn->fd, host, &priv->ev,
- priv->timeout, rspamd_http_event_handler,
- rspamd_http_ssl_err_handler, conn)) {
-
- err = g_error_new(HTTP_ERROR, 400,
- "ssl connection error: ssl error=%s, errno=%s",
- ERR_error_string(ERR_get_error(), NULL),
- strerror(errno));
- rspamd_http_connection_ref(conn);
- conn->error_handler(conn, err);
- rspamd_http_connection_unref(conn);
- g_error_free(err);
- return FALSE;
- }
- }
- else {
- /* Just restore SSL handlers */
- rspamd_ssl_connection_restore_handlers(priv->ssl,
- rspamd_http_event_handler,
- rspamd_http_ssl_err_handler,
- conn,
- EV_WRITE);
+ if (!priv->ssl) {
+ priv->ssl = rspamd_ssl_connection_new(ssl_ctx, priv->ctx->event_loop,
+ !(msg->flags & RSPAMD_HTTP_FLAG_SSL_NOVERIFY),
+ conn->log_tag);
+ g_assert(priv->ssl != NULL);
+
+ if (!rspamd_ssl_connect_fd(priv->ssl, conn->fd, host, &priv->ev,
+ priv->timeout, rspamd_http_event_handler,
+ rspamd_http_ssl_err_handler, conn)) {
+
+ err = g_error_new(HTTP_ERROR, 400,
+ "ssl connection error: ssl error=%s, errno=%s",
+ ERR_error_string(ERR_get_error(), NULL),
+ strerror(errno));
+ rspamd_http_connection_ref(conn);
+ conn->error_handler(conn, err);
+ rspamd_http_connection_unref(conn);
+ g_error_free(err);
+ return FALSE;
}
+ }
+ else {
+ /* Just restore SSL handlers */
+ rspamd_ssl_connection_restore_handlers(priv->ssl,
+ rspamd_http_event_handler,
+ rspamd_http_ssl_err_handler,
+ conn,
+ EV_WRITE);
+ }
}
}
else {
@@ -2467,10 +2452,10 @@ rspamd_http_connection_get_peer_key(struct rspamd_http_connection *conn)
struct rspamd_http_connection_private *priv = conn->priv;
if (priv->peer_key) {
- return priv->peer_key;
+ return priv->peer_key;
}
else if (priv->msg) {
- return priv->msg->peer_key;
+ return priv->msg->peer_key;
}
return NULL;
@@ -2482,10 +2467,10 @@ rspamd_http_connection_is_encrypted(struct rspamd_http_connection *conn)
struct rspamd_http_connection_private *priv = conn->priv;
if (priv->peer_key != NULL) {
- return TRUE;
+ return TRUE;
}
else if (priv->msg) {
- return priv->msg->peer_key != NULL;
+ return priv->msg->peer_key != NULL;
}
return FALSE;
@@ -2512,103 +2497,103 @@ rspamd_http_message_parse_query(struct rspamd_http_message *msg)
rspamd_fstring_mapped_ftok_free);
if (msg->url && msg->url->len > 0) {
- http_parser_parse_url(msg->url->str, msg->url->len, TRUE, &u);
-
- if (u.field_set & (1 << UF_QUERY)) {
- p = msg->url->str + u.field_data[UF_QUERY].off;
- c = p;
- end = p + u.field_data[UF_QUERY].len;
-
- while (p <= end) {
- switch (state) {
- case parse_key:
- if ((p == end || *p == '&') && p > c) {
- /* We have a single parameter without a value */
- key = rspamd_fstring_new_init(c, p - c);
- key_tok = rspamd_ftok_map(key);
- key_tok->len = rspamd_url_decode(key->str, key->str,
- key->len);
-
- value = rspamd_fstring_new_init("", 0);
- value_tok = rspamd_ftok_map(value);
+ http_parser_parse_url(msg->url->str, msg->url->len, TRUE, &u);
+
+ if (u.field_set & (1 << UF_QUERY)) {
+ p = msg->url->str + u.field_data[UF_QUERY].off;
+ c = p;
+ end = p + u.field_data[UF_QUERY].len;
+
+ while (p <= end) {
+ switch (state) {
+ case parse_key:
+ if ((p == end || *p == '&') && p > c) {
+ /* We have a single parameter without a value */
+ key = rspamd_fstring_new_init(c, p - c);
+ key_tok = rspamd_ftok_map(key);
+ key_tok->len = rspamd_url_decode(key->str, key->str,
+ key->len);
+
+ value = rspamd_fstring_new_init("", 0);
+ value_tok = rspamd_ftok_map(value);
+
+ g_hash_table_replace(res, key_tok, value_tok);
+ state = parse_ampersand;
+ }
+ else if (*p == '=' && p > c) {
+ /* We have something like key=value */
+ key = rspamd_fstring_new_init(c, p - c);
+ key_tok = rspamd_ftok_map(key);
+ key_tok->len = rspamd_url_decode(key->str, key->str,
+ key->len);
+
+ state = parse_eqsign;
+ }
+ else {
+ p++;
+ }
+ break;
- g_hash_table_replace(res, key_tok, value_tok);
- state = parse_ampersand;
- }
- else if (*p == '=' && p > c) {
- /* We have something like key=value */
- key = rspamd_fstring_new_init(c, p - c);
- key_tok = rspamd_ftok_map(key);
- key_tok->len = rspamd_url_decode(key->str, key->str,
- key->len);
-
- state = parse_eqsign;
- }
- else {
- p++;
- }
- break;
+ case parse_eqsign:
+ if (*p != '=') {
+ c = p;
+ state = parse_value;
+ }
+ else {
+ p++;
+ }
+ break;
- case parse_eqsign:
- if (*p != '=') {
- c = p;
- state = parse_value;
- }
- else {
- p++;
- }
- break;
-
- case parse_value:
- if ((p == end || *p == '&') && p >= c) {
- g_assert(key != NULL);
- if (p > c) {
- value = rspamd_fstring_new_init(c, p - c);
- value_tok = rspamd_ftok_map(value);
- value_tok->len = rspamd_url_decode(value->str,
- value->str,
- value->len);
- /* Detect quotes for value */
- if (value_tok->begin[0] == '"') {
- memmove(value->str, value->str + 1,
- value_tok->len - 1);
- value_tok->len--;
- }
- if (value_tok->begin[value_tok->len - 1] == '"') {
- value_tok->len--;
- }
+ case parse_value:
+ if ((p == end || *p == '&') && p >= c) {
+ g_assert(key != NULL);
+ if (p > c) {
+ value = rspamd_fstring_new_init(c, p - c);
+ value_tok = rspamd_ftok_map(value);
+ value_tok->len = rspamd_url_decode(value->str,
+ value->str,
+ value->len);
+ /* Detect quotes for value */
+ if (value_tok->begin[0] == '"') {
+ memmove(value->str, value->str + 1,
+ value_tok->len - 1);
+ value_tok->len--;
}
- else {
- value = rspamd_fstring_new_init("", 0);
- value_tok = rspamd_ftok_map(value);
+ if (value_tok->begin[value_tok->len - 1] == '"') {
+ value_tok->len--;
}
-
- g_hash_table_replace(res, key_tok, value_tok);
- key = value = NULL;
- key_tok = value_tok = NULL;
- state = parse_ampersand;
}
else {
- p++;
+ value = rspamd_fstring_new_init("", 0);
+ value_tok = rspamd_ftok_map(value);
}
- break;
- case parse_ampersand:
- if (p != end && *p != '&') {
- c = p;
- state = parse_key;
- }
- else {
- p++;
- }
- break;
+ g_hash_table_replace(res, key_tok, value_tok);
+ key = value = NULL;
+ key_tok = value_tok = NULL;
+ state = parse_ampersand;
+ }
+ else {
+ p++;
+ }
+ break;
+
+ case parse_ampersand:
+ if (p != end && *p != '&') {
+ c = p;
+ state = parse_key;
+ }
+ else {
+ p++;
}
+ break;
}
}
+ }
- if (state != parse_ampersand && key != NULL) {
- rspamd_fstring_free(key);
- }
+ if (state != parse_ampersand && key != NULL) {
+ rspamd_fstring_free(key);
+ }
}
return res;
@@ -2635,15 +2620,15 @@ void rspamd_http_connection_disable_encryption(struct rspamd_http_connection *co
priv = conn->priv;
if (priv) {
- if (priv->local_key) {
- rspamd_keypair_unref(priv->local_key);
- }
- if (priv->peer_key) {
- rspamd_pubkey_unref(priv->peer_key);
- }
+ if (priv->local_key) {
+ rspamd_keypair_unref(priv->local_key);
+ }
+ if (priv->peer_key) {
+ rspamd_pubkey_unref(priv->peer_key);
+ }
- priv->local_key = NULL;
- priv->peer_key = NULL;
- priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_ENCRYPTED;
+ priv->local_key = NULL;
+ priv->peer_key = NULL;
+ priv->flags &= ~RSPAMD_HTTP_CONN_FLAG_ENCRYPTED;
}
} \ No newline at end of file
diff --git a/src/libserver/http/http_context.c b/src/libserver/http/http_context.c
index fe9412fe2..df32a2258 100644
--- a/src/libserver/http/http_context.c
+++ b/src/libserver/http/http_context.c
@@ -77,8 +77,7 @@ rspamd_http_context_client_rotate_ev(struct ev_loop *loop, ev_timer *w, int reve
ev_timer_again(loop, w);
kp = ctx->client_kp;
- ctx->client_kp = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ ctx->client_kp = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
rspamd_keypair_unref(kp);
}
diff --git a/src/libserver/logger/logger.c b/src/libserver/logger/logger.c
index 13bac5cbe..25818e7a5 100644
--- a/src/libserver/logger/logger.c
+++ b/src/libserver/logger/logger.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Vsevolod Stakhov
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -277,8 +277,7 @@ rspamd_log_open_specific(rspamd_mempool_t *pool,
if (cfg->log_encryption_key) {
logger->pk = rspamd_pubkey_ref(cfg->log_encryption_key);
- logger->keypair = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ logger->keypair = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
rspamd_pubkey_calculate_nm(logger->pk, logger->keypair);
}
}
@@ -342,9 +341,9 @@ rspamd_log_encrypt_message(const char *begin, const char *end, gsize *enc_len,
g_assert(end > begin);
/* base64 (pubkey | nonce | message) */
- inlen = rspamd_cryptobox_nonce_bytes(RSPAMD_CRYPTOBOX_MODE_25519) +
- rspamd_cryptobox_pk_bytes(RSPAMD_CRYPTOBOX_MODE_25519) +
- rspamd_cryptobox_mac_bytes(RSPAMD_CRYPTOBOX_MODE_25519) +
+ inlen = crypto_box_noncebytes() +
+ crypto_box_publickeybytes() +
+ crypto_box_macbytes() +
(end - begin);
out = g_malloc(inlen);
@@ -352,16 +351,15 @@ rspamd_log_encrypt_message(const char *begin, const char *end, gsize *enc_len,
comp = rspamd_pubkey_get_pk(rspamd_log->pk, &len);
memcpy(p, comp, len);
p += len;
- ottery_rand_bytes(p, rspamd_cryptobox_nonce_bytes(RSPAMD_CRYPTOBOX_MODE_25519));
+ ottery_rand_bytes(p, crypto_box_noncebytes());
nonce = p;
- p += rspamd_cryptobox_nonce_bytes(RSPAMD_CRYPTOBOX_MODE_25519);
+ p += crypto_box_noncebytes();
mac = p;
- p += rspamd_cryptobox_mac_bytes(RSPAMD_CRYPTOBOX_MODE_25519);
+ p += crypto_box_macbytes();
memcpy(p, begin, end - begin);
comp = rspamd_pubkey_get_nm(rspamd_log->pk, rspamd_log->keypair);
g_assert(comp != NULL);
- rspamd_cryptobox_encrypt_nm_inplace(p, end - begin, nonce, comp, mac,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_encrypt_nm_inplace(p, end - begin, nonce, comp, mac);
b64 = rspamd_encode_base64(out, inlen, 0, enc_len);
g_free(out);
diff --git a/src/libserver/maps/map.c b/src/libserver/maps/map.c
index 15fce7e9d..631455755 100644
--- a/src/libserver/maps/map.c
+++ b/src/libserver/maps/map.c
@@ -670,14 +670,14 @@ rspamd_map_check_sig_pk_mem(const unsigned char *sig,
GString *b32_key;
gboolean ret = TRUE;
- if (siglen != rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519)) {
+ if (siglen != crypto_sign_bytes()) {
msg_err_map("can't open signature for %s: invalid size: %z", map->name, siglen);
ret = FALSE;
}
if (ret && !rspamd_cryptobox_verify(sig, siglen, input, inlen,
- rspamd_pubkey_get_pk(pk, NULL), RSPAMD_CRYPTOBOX_MODE_25519)) {
+ rspamd_pubkey_get_pk(pk, NULL))) {
msg_err_map("can't verify signature for %s: incorrect signature", map->name);
ret = FALSE;
@@ -718,8 +718,7 @@ rspamd_map_check_file_sig(const char *fname,
return FALSE;
}
- pk = rspamd_pubkey_from_base32(data, len, RSPAMD_KEYPAIR_SIGN,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ pk = rspamd_pubkey_from_base32(data, len, RSPAMD_KEYPAIR_SIGN);
munmap(data, len);
if (pk == NULL) {
@@ -2414,8 +2413,7 @@ rspamd_map_check_proto(struct rspamd_config *cfg,
end_key = memchr(pos, '+', end - pos);
if (end_key != NULL) {
- bk->trusted_pubkey = rspamd_pubkey_from_base32(pos, end_key - pos,
- RSPAMD_KEYPAIR_SIGN, RSPAMD_CRYPTOBOX_MODE_25519);
+ bk->trusted_pubkey = rspamd_pubkey_from_base32(pos, end_key - pos, RSPAMD_KEYPAIR_SIGN);
if (bk->trusted_pubkey == NULL) {
msg_err_config("cannot read pubkey from map: %s",
@@ -2426,8 +2424,7 @@ rspamd_map_check_proto(struct rspamd_config *cfg,
}
else if (end - pos > 64) {
/* Try hex encoding */
- bk->trusted_pubkey = rspamd_pubkey_from_hex(pos, 64,
- RSPAMD_KEYPAIR_SIGN, RSPAMD_CRYPTOBOX_MODE_25519);
+ bk->trusted_pubkey = rspamd_pubkey_from_hex(pos, 64, RSPAMD_KEYPAIR_SIGN);
if (bk->trusted_pubkey == NULL) {
msg_err_config("cannot read pubkey from map: %s",
diff --git a/src/libserver/milter.c b/src/libserver/milter.c
index f35278a0e..94b0d6cc1 100644
--- a/src/libserver/milter.c
+++ b/src/libserver/milter.c
@@ -1465,10 +1465,16 @@ rspamd_milter_macro_http(struct rspamd_milter_session *session,
return;
}
+ /*
+ * When we get a queue-id we try to pass it to the backend, where possible
+ * We also need that for logging consistency
+ */
IF_MACRO("{i}")
{
rspamd_http_message_add_header_len(msg, QUEUE_ID_HEADER,
found->begin, found->len);
+ rspamd_http_message_add_header_len(msg, LOG_TAG_HEADER,
+ found->begin, found->len);
}
else
{
@@ -1476,6 +1482,8 @@ rspamd_milter_macro_http(struct rspamd_milter_session *session,
{
rspamd_http_message_add_header_len(msg, QUEUE_ID_HEADER,
found->begin, found->len);
+ rspamd_http_message_add_header_len(msg, LOG_TAG_HEADER,
+ found->begin, found->len);
}
}
diff --git a/src/libserver/protocol.c b/src/libserver/protocol.c
index db83b0bfb..ee2192913 100644
--- a/src/libserver/protocol.c
+++ b/src/libserver/protocol.c
@@ -660,9 +660,9 @@ rspamd_protocol_handle_headers(struct rspamd_task *task,
IF_HEADER(USER_HEADER)
{
/*
- * We must ignore User header in case of spamc, as SA has
- * different meaning of this header
- */
+ * We must ignore User header in case of spamc, as SA has
+ * different meaning of this header
+ */
msg_debug_protocol("read user header, value: %T", hv_tok);
if (!RSPAMD_TASK_IS_SPAMC(task)) {
task->auth_user = rspamd_mempool_ftokdup(task->task_pool,
@@ -708,6 +708,15 @@ rspamd_protocol_handle_headers(struct rspamd_task *task,
task->flags |= RSPAMD_TASK_FLAG_NO_LOG;
}
}
+ IF_HEADER(LOG_TAG_HEADER)
+ {
+ msg_debug_protocol("read log-tag header, value: %T", hv_tok);
+ /* Ensure that a tag is valid */
+ if (rspamd_fast_utf8_validate(hv_tok->begin, hv_tok->len) == 0) {
+ memcpy(task->task_pool->tag.uid, hv_tok->begin,
+ MIN(hv_tok->len, sizeof(task->task_pool->tag.uid)));
+ }
+ }
break;
case 'm':
case 'M':
@@ -752,9 +761,9 @@ rspamd_protocol_handle_headers(struct rspamd_task *task,
default:
msg_debug_protocol("generic header: %T", hn_tok);
break;
- }
+ }
- rspamd_task_add_request_header (task, hn_tok, hv_tok);
+ rspamd_task_add_request_header (task, hn_tok, hv_tok);
}
}); /* End of kh_foreach_value */
diff --git a/src/libserver/protocol_internal.h b/src/libserver/protocol_internal.h
index 7a70ccef0..e55e54851 100644
--- a/src/libserver/protocol_internal.h
+++ b/src/libserver/protocol_internal.h
@@ -78,6 +78,7 @@ extern "C" {
#define HOSTNAME_HEADER "Hostname"
#define DELIVER_TO_HEADER "Deliver-To"
#define NO_LOG_HEADER "Log"
+#define LOG_TAG_HEADER "Log-Tag"
#define MLEN_HEADER "Message-Length"
#define USER_AGENT_HEADER "User-Agent"
#define MTA_TAG_HEADER "MTA-Tag"
diff --git a/src/libserver/spf.c b/src/libserver/spf.c
index 32c020bf3..562222042 100644
--- a/src/libserver/spf.c
+++ b/src/libserver/spf.c
@@ -451,6 +451,9 @@ rspamd_spf_process_reference(struct spf_resolved *target,
target->flags |= RSPAMD_SPF_RESOLVED_NA;
continue;
}
+ if (cur->flags & RSPAMD_SPF_FLAG_PLUSALL) {
+ target->flags |= RSPAMD_SPF_RESOLVED_PLUSALL;
+ }
if (cur->flags & RSPAMD_SPF_FLAG_INVALID) {
/* Ignore invalid elements */
continue;
@@ -1418,7 +1421,7 @@ parse_spf_all(struct spf_record *rec, struct spf_addr *addr)
/* Disallow +all */
if (addr->mech == SPF_PASS) {
- addr->flags |= RSPAMD_SPF_FLAG_INVALID;
+ addr->flags |= RSPAMD_SPF_FLAG_PLUSALL;
msg_notice_spf("domain %s allows any SPF (+all), ignore SPF record completely",
rec->sender_domain);
}
diff --git a/src/libserver/spf.h b/src/libserver/spf.h
index cc0ee4c05..b89dc4d0e 100644
--- a/src/libserver/spf.h
+++ b/src/libserver/spf.h
@@ -77,6 +77,7 @@ typedef enum spf_action_e {
#define RSPAMD_SPF_FLAG_PERMFAIL (1u << 10u)
#define RSPAMD_SPF_FLAG_RESOLVED (1u << 11u)
#define RSPAMD_SPF_FLAG_CACHED (1u << 12u)
+#define RSPAMD_SPF_FLAG_PLUSALL (1u << 13u)
/** Default SPF limits for avoiding abuse **/
#define SPF_MAX_NESTING 10
@@ -104,6 +105,7 @@ enum rspamd_spf_resolved_flags {
RSPAMD_SPF_RESOLVED_TEMP_FAILED = (1u << 0u),
RSPAMD_SPF_RESOLVED_PERM_FAILED = (1u << 1u),
RSPAMD_SPF_RESOLVED_NA = (1u << 2u),
+ RSPAMD_SPF_RESOLVED_PLUSALL = (1u << 3u),
};
struct spf_resolved {
diff --git a/src/libserver/symcache/symcache_runtime.cxx b/src/libserver/symcache/symcache_runtime.cxx
index dc7066b32..ddfbdf1ae 100644
--- a/src/libserver/symcache/symcache_runtime.cxx
+++ b/src/libserver/symcache/symcache_runtime.cxx
@@ -333,8 +333,8 @@ auto symcache_runtime::process_pre_postfilters(struct rspamd_task *task,
*/
if (stage != RSPAMD_TASK_STAGE_IDEMPOTENT &&
!(item->flags & SYMBOL_TYPE_IGNORE_PASSTHROUGH)) {
- if (check_metric_limit(task)) {
- msg_debug_cache_task_lambda("task has already the result being set, ignore further checks");
+ if (check_process_status(task) == check_status::passthrough) {
+ msg_debug_cache_task_lambda("task has already the passthrough result being set, ignore further checks");
return true;
}
@@ -407,13 +407,20 @@ auto symcache_runtime::process_filters(struct rspamd_task *task, symcache &cache
break;
}
+ auto check_result = check_process_status(task);
+
if (!(item->flags & (SYMBOL_TYPE_FINE | SYMBOL_TYPE_IGNORE_PASSTHROUGH))) {
- if (has_passtrough || check_metric_limit(task)) {
- msg_debug_cache_task_lambda("task has already the result being set, ignore further checks");
+ if (has_passtrough || check_result == check_status::passthrough) {
+ msg_debug_cache_task_lambda("task has already the passthrough result being set, ignore further checks");
has_passtrough = true;
/* Skip this item */
continue;
}
+ else if (check_result == check_status::limit_reached) {
+ msg_debug_cache_task_lambda("task has already the limit reached result being set, ignore further checks");
+ /* Skip this item */
+ continue;
+ }
}
auto dyn_item = &dynamic_items[idx];
@@ -531,19 +538,8 @@ auto symcache_runtime::process_symbol(struct rspamd_task *task, symcache &cache,
return true;
}
-auto symcache_runtime::check_metric_limit(struct rspamd_task *task) -> bool
+auto symcache_runtime::check_process_status(struct rspamd_task *task) -> symcache_runtime::check_status
{
- if (task->flags & RSPAMD_TASK_FLAG_PASS_ALL) {
- return false;
- }
-
- /* Check score limit */
- if (!std::isnan(lim)) {
- if (task->result->score > lim) {
- return true;
- }
- }
-
if (task->result->passthrough_result != nullptr) {
/* We also need to check passthrough results */
auto *pr = task->result->passthrough_result;
@@ -563,11 +559,22 @@ auto symcache_runtime::check_metric_limit(struct rspamd_task *task) -> bool
}
/* Immediately stop on non least passthrough action */
- return true;
+ return check_status::passthrough;
}
}
- return false;
+ if (task->flags & RSPAMD_TASK_FLAG_PASS_ALL) {
+ return check_status::allow;
+ }
+
+ /* Check score limit */
+ if (!std::isnan(lim)) {
+ if (task->result->score > lim) {
+ return check_status::limit_reached;
+ }
+ }
+
+ return check_status::allow;
}
auto symcache_runtime::check_item_deps(struct rspamd_task *task, symcache &cache, cache_item *item,
diff --git a/src/libserver/symcache/symcache_runtime.hxx b/src/libserver/symcache/symcache_runtime.hxx
index 7e4a41269..d1dc7ac24 100644
--- a/src/libserver/symcache/symcache_runtime.hxx
+++ b/src/libserver/symcache/symcache_runtime.hxx
@@ -60,6 +60,11 @@ class symcache_runtime {
enabled = 1,
disabled = 2,
} slow_status;
+ enum class check_status {
+ allow,
+ limit_reached,
+ passthrough,
+ };
bool profile;
double profile_start;
@@ -77,7 +82,7 @@ class symcache_runtime {
/* Specific stages of the processing */
auto process_pre_postfilters(struct rspamd_task *task, symcache &cache, int start_events, unsigned int stage) -> bool;
auto process_filters(struct rspamd_task *task, symcache &cache, int start_events) -> bool;
- auto check_metric_limit(struct rspamd_task *task) -> bool;
+ auto check_process_status(struct rspamd_task *task) -> check_status;
auto check_item_deps(struct rspamd_task *task, symcache &cache, cache_item *item,
cache_dynamic_item *dyn_item, bool check_only) -> bool;
diff --git a/src/libstat/stat_process.c b/src/libstat/stat_process.c
index ad976e713..5db3af6ce 100644
--- a/src/libstat/stat_process.c
+++ b/src/libstat/stat_process.c
@@ -509,6 +509,14 @@ rspamd_stat_classify(struct rspamd_task *task, lua_State *L, unsigned int stage,
return ret;
}
+ if (task->message == NULL) {
+ ret = RSPAMD_STAT_PROCESS_ERROR;
+ msg_err_task("trying to classify empty message");
+
+ task->processed_stages |= stage;
+ return ret;
+ }
+
if (stage == RSPAMD_TASK_STAGE_CLASSIFIERS_PRE) {
/* Preprocess tokens */
rspamd_stat_preprocess(st_ctx, task, FALSE, FALSE);
@@ -892,6 +900,18 @@ rspamd_stat_learn(struct rspamd_task *task,
return ret;
}
+
+ if (task->message == NULL) {
+ ret = RSPAMD_STAT_PROCESS_ERROR;
+ if (err && *err == NULL) {
+ g_set_error(err, rspamd_stat_quark(), 500,
+ "Trying to learn an empty message");
+ }
+
+ task->processed_stages |= stage;
+ return ret;
+ }
+
if (stage == RSPAMD_TASK_STAGE_LEARN_PRE) {
/* Process classifiers */
rspamd_stat_preprocess(st_ctx, task, TRUE, spam);
diff --git a/src/lua/lua_config.c b/src/lua/lua_config.c
index f5405f76d..e3f8b2e57 100644
--- a/src/lua/lua_config.c
+++ b/src/lua/lua_config.c
@@ -34,7 +34,11 @@
local function foo(task)
-- do something
end
-rspamd_config:register_symbol('SYMBOL', 1.0, foo)
+rspamd_config:register_symbol{
+ name = 'SYMBOL',
+ score = 1.0,
+ callback = foo
+}
-- Get configuration
local tab = rspamd_config:get_all_opt('module') -- get table for module's options
@@ -1965,210 +1969,454 @@ lua_config_get_symbol_flags(lua_State *L)
return 1;
}
-static int
-lua_config_register_symbol(lua_State *L)
+static bool
+lua_config_register_symbol_from_table(lua_State *L, struct rspamd_config *cfg,
+ const char *name, int tbl_idx, int *id_out)
{
- LUA_TRACE_POINT;
- struct rspamd_config *cfg = lua_check_config(L, 1);
- const char *name = NULL, *type_str = NULL,
- *description = NULL, *group = NULL;
- double weight = 0, score = NAN, parent_float = NAN;
- gboolean one_shot = FALSE;
- int ret = -1, cbref = -1;
- unsigned int type = 0, flags = 0;
- int64_t parent = 0, priority = 0, nshots = 0;
+ unsigned int type = SYMBOL_TYPE_NORMAL, priority = 0;
+ double weight = 1.0, score = NAN;
+ const char *type_str, *group = NULL, *description = NULL;
GArray *allowed_ids = NULL, *forbidden_ids = NULL;
- GError *err = NULL;
- int prev_top = lua_gettop(L);
+ int id, nshots, cb_ref, parent = -1;
+ unsigned int flags = 0;
+ gboolean optional = FALSE;
- if (cfg) {
- if (!rspamd_lua_parse_table_arguments(L, 2, &err,
- RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
- "name=S;weight=N;callback=F;type=S;priority=I;parent=D;"
- "score=D;description=S;group=S;one_shot=B;nshots=I",
- &name, &weight, &cbref, &type_str,
- &priority, &parent_float,
- &score, &description, &group, &one_shot, &nshots)) {
- msg_err_config("bad arguments: %e", err);
- g_error_free(err);
- lua_settop(L, prev_top);
+ /*
+ * Table can have the following attributes:
+ * "callback" - should be a callback function
+ * "weight" - optional weight
+ * "priority" - optional priority
+ * "type" - optional type (normal, virtual, callback)
+ * "flags" - optional flags
+ * -- Metric options
+ * "score" - optional default score (overridden by metric)
+ * "group" - optional default group
+ * "one_shot" - optional one shot mode
+ * "description" - optional description
+ */
+ lua_pushvalue(L, tbl_idx); /* Push table on top of the stack */
- return luaL_error(L, "invalid arguments");
- }
+ if (name == NULL) {
+ /* Try to resolve name */
+ lua_pushstring(L, "name");
+ lua_gettable(L, -2);
- /* Deal with flags and ids */
- lua_pushstring(L, "flags");
- lua_gettable(L, 2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- flags = lua_parse_symbol_flags(lua_tostring(L, -1));
- }
- else if (lua_type(L, -1) == LUA_TTABLE) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- flags |= lua_parse_symbol_flags(lua_tostring(L, -1));
- }
- }
- lua_pop(L, 1); /* Clean flags */
+ if (lua_type(L, -1) != LUA_TSTRING) {
+ lua_pop(L, 2);
+ luaL_error(L, "name is not specified");
- lua_pushstring(L, "allowed_ids");
- lua_gettable(L, 2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- allowed_ids = rspamd_process_id_list(lua_tostring(L, -1));
+ return false;
}
- else if (lua_type(L, -1) == LUA_TTABLE) {
- allowed_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
- rspamd_lua_table_size(L, -1));
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- uint32_t v = lua_tointeger(L, -1);
- g_array_append_val(allowed_ids, v);
- }
+ else {
+ name = lua_tostring(L, -1);
}
+
lua_pop(L, 1);
+ }
- lua_pushstring(L, "forbidden_ids");
- lua_gettable(L, 2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- forbidden_ids = rspamd_process_id_list(lua_tostring(L, -1));
+ lua_pushstring(L, "callback");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) != LUA_TFUNCTION) {
+ cb_ref = -1;
+ }
+ else {
+ cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+ lua_pop(L, 1);
+
+ /* Optional fields */
+ lua_pushstring(L, "weight");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ weight = lua_tonumber(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "priority");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ priority = lua_tointeger(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "optional");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TBOOLEAN) {
+ optional = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "type");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ type_str = lua_tostring(L, -1);
+ }
+ else {
+ type_str = "normal";
+ }
+ lua_pop(L, 1);
+
+ type = lua_parse_symbol_type(type_str);
+
+ if (!name && !(type & SYMBOL_TYPE_CALLBACK)) {
+ luaL_error(L, "no symbol name but type is not callback");
+
+ return false;
+ }
+ else if (!(type & SYMBOL_TYPE_VIRTUAL) && cb_ref == -1) {
+ luaL_error(L, "no callback for symbol %s", name);
+
+ return false;
+ }
+
+ lua_pushstring(L, "parent");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ parent = lua_tointeger(L, -1);
+ }
+ lua_pop(L, 1);
+
+
+ /* Deal with flags and ids */
+ lua_pushstring(L, "flags");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ flags = lua_parse_symbol_flags(lua_tostring(L, -1));
+ }
+ else if (lua_type(L, -1) == LUA_TTABLE) {
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ flags |= lua_parse_symbol_flags(lua_tostring(L, -1));
}
- else if (lua_type(L, -1) == LUA_TTABLE) {
- forbidden_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
- rspamd_lua_table_size(L, -1));
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- uint32_t v = lua_tointeger(L, -1);
- g_array_append_val(forbidden_ids, v);
- }
+ }
+ lua_pop(L, 1); /* Clean flags */
+
+ lua_pushstring(L, "allowed_ids");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ allowed_ids = rspamd_process_id_list(lua_tostring(L, -1));
+ }
+ else if (lua_type(L, -1) == LUA_TTABLE) {
+ allowed_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
+ rspamd_lua_table_size(L, -1));
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ uint32_t v = lua_tointeger(L, -1);
+ g_array_append_val(allowed_ids, v);
}
- lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
- if (nshots == 0) {
- nshots = cfg->default_max_shots;
+ lua_pushstring(L, "forbidden_ids");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ forbidden_ids = rspamd_process_id_list(lua_tostring(L, -1));
+ }
+ else if (lua_type(L, -1) == LUA_TTABLE) {
+ forbidden_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
+ rspamd_lua_table_size(L, -1));
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ uint32_t v = lua_tointeger(L, -1);
+ g_array_append_val(forbidden_ids, v);
}
+ }
+ lua_pop(L, 1);
- type = lua_parse_symbol_type(type_str);
+ id = rspamd_register_symbol_fromlua(L,
+ cfg,
+ name,
+ cb_ref,
+ weight,
+ priority,
+ type | flags,
+ parent,
+ allowed_ids, forbidden_ids,
+ optional);
- if (!name && !(type & SYMBOL_TYPE_CALLBACK)) {
- lua_settop(L, prev_top);
- return luaL_error(L, "no symbol name but type is not callback");
- }
- else if (!(type & SYMBOL_TYPE_VIRTUAL) && cbref == -1) {
- lua_settop(L, prev_top);
- return luaL_error(L, "no callback for symbol %s", name);
- }
+ if (allowed_ids) {
+ g_array_free(allowed_ids, TRUE);
+ }
+
+ if (forbidden_ids) {
+ g_array_free(forbidden_ids, TRUE);
+ }
+
+ if (id != -1) {
+ if (cb_ref != -1) {
+ /* Check for condition */
+ lua_pushstring(L, "condition");
+ lua_gettable(L, -2);
- if (isnan(parent_float)) {
- parent = -1;
+ if (lua_type(L, -1) == LUA_TFUNCTION) {
+ int condref;
+
+ /* Here we pop function from the stack, so no lua_pop is required */
+ condref = luaL_ref(L, LUA_REGISTRYINDEX);
+ g_assert(name != NULL);
+ rspamd_symcache_add_condition_delayed(cfg->cache,
+ name, L, condref);
+ }
+ else {
+ lua_pop(L, 1);
+ }
}
- else {
- parent = parent_float;
+
+ /* Check for augmentations */
+ lua_pushstring(L, "augmentations");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TTABLE) {
+
+ int aug_tbl_idx = lua_gettop(L);
+ for (lua_pushnil(L); lua_next(L, aug_tbl_idx); lua_pop(L, 1)) {
+ rspamd_symcache_add_symbol_augmentation(cfg->cache, id,
+ lua_tostring(L, -1), NULL);
+ }
}
- ret = rspamd_register_symbol_fromlua(L,
- cfg,
- name,
- cbref,
- weight == 0 ? 1.0 : weight,
- priority,
- type | flags,
- parent,
- allowed_ids, forbidden_ids,
- FALSE);
+ lua_pop(L, 1);
+ }
- if (allowed_ids) {
- g_array_free(allowed_ids, TRUE);
+ /*
+ * Now check if a symbol has not been registered in any metric and
+ * insert default value if applicable
+ */
+ struct rspamd_symbol *sym = g_hash_table_lookup(cfg->symbols, name);
+ if (sym == NULL || (sym->flags & RSPAMD_SYMBOL_FLAG_UNSCORED)) {
+ nshots = cfg->default_max_shots;
+
+ lua_pushstring(L, "score");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ score = lua_tonumber(L, -1);
+
+ if (sym) {
+ /* Reset unscored flag */
+ sym->flags &= ~RSPAMD_SYMBOL_FLAG_UNSCORED;
+ }
}
+ lua_pop(L, 1);
- if (forbidden_ids) {
- g_array_free(forbidden_ids, TRUE);
+ lua_pushstring(L, "group");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ group = lua_tostring(L, -1);
}
+ lua_pop(L, 1);
+
+ if (!isnan(score) || group != NULL) {
+ lua_pushstring(L, "description");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ description = lua_tostring(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "one_shot");
+ lua_gettable(L, -2);
- if (ret != -1) {
- if (!isnan(score) || group) {
- if (one_shot) {
+ if (lua_type(L, -1) == LUA_TBOOLEAN) {
+ if (lua_toboolean(L, -1)) {
nshots = 1;
}
+ }
+ lua_pop(L, 1);
- rspamd_config_add_symbol(cfg, name,
- score, description, group, flags,
- 0, nshots);
+ lua_pushstring(L, "one_param");
+ lua_gettable(L, -2);
- lua_pushstring(L, "groups");
- lua_gettable(L, 2);
+ if (lua_type(L, -1) == LUA_TBOOLEAN) {
+ if (lua_toboolean(L, -1)) {
+ flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM;
+ }
+ }
+ lua_pop(L, 1);
- if (lua_istable(L, -1)) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- if (lua_isstring(L, -1)) {
- rspamd_config_add_symbol_group(cfg, name,
- lua_tostring(L, -1));
- }
- else {
- lua_settop(L, prev_top);
- return luaL_error(L, "invalid groups element");
- }
+ /*
+ * Do not override the existing symbols (using zero priority),
+ * since we are defining default values here
+ */
+ if (!isnan(score)) {
+ rspamd_config_add_symbol(cfg, name, score,
+ description, group, flags, 0, nshots);
+ }
+ else if (group) {
+ /* Add with zero score */
+ rspamd_config_add_symbol(cfg, name, NAN,
+ description, group, flags, 0, nshots);
+ }
+
+ lua_pushstring(L, "groups");
+ lua_gettable(L, -2);
+
+ if (lua_istable(L, -1)) {
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ if (lua_isstring(L, -1)) {
+ rspamd_config_add_symbol_group(cfg, name,
+ lua_tostring(L, -1));
+ }
+ else {
+ lua_pop(L, 2);
+ luaL_error(L, "invalid groups element");
+
+ return false;
}
}
+ }
- lua_pop(L, 1);
+ lua_pop(L, 1);
+ }
+ }
+ else {
+ /* Fill in missing fields from lua definition if they are not set */
+ if (sym->description == NULL) {
+ lua_pushstring(L, "description");
+ lua_gettable(L, -2);
+
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ description = lua_tostring(L, -1);
}
+ lua_pop(L, 1);
- lua_pushstring(L, "augmentations");
- lua_gettable(L, 2);
+ if (description) {
+ sym->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
+ }
+ }
- if (lua_type(L, -1) == LUA_TTABLE) {
- int tbl_idx = lua_gettop(L);
- for (lua_pushnil(L); lua_next(L, tbl_idx); lua_pop(L, 1)) {
- size_t len;
- const char *augmentation = lua_tolstring(L, -1, &len), *eqsign_pos;
+ /* If ungrouped and there is a group defined in lua, change the primary group
+ * Otherwise, add to the list of groups for this symbol. */
+ lua_pushstring(L, "group");
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ group = lua_tostring(L, -1);
+ }
+ lua_pop(L, 1);
+ if (group) {
+ if (sym->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED) {
+ /* Unset the "ungrouped" group */
+ sym->gr = NULL;
+ }
+ /* Add the group. If the symbol was ungrouped, this will
+ * clear RSPAMD_SYMBOL_FLAG_UNGROUPED from the flags. */
+ rspamd_config_add_symbol_group(cfg, name, group);
+ }
+ }
- /* Find `=` symbol and use it as a separator */
- eqsign_pos = memchr(augmentation, '=', len);
- if (eqsign_pos != NULL && eqsign_pos + 1 < augmentation + len) {
- rspamd_ftok_t tok;
+ /* Remove table from stack */
+ lua_pop(L, 1);
- tok.begin = augmentation;
- tok.len = eqsign_pos - augmentation;
- char *augentation_name = rspamd_ftokdup(&tok);
+ *id_out = id;
- tok.begin = eqsign_pos + 1;
- tok.len = (augmentation + len) - tok.begin;
+ return true;
+}
- char *augmentation_value = rspamd_ftokdup(&tok);
+/* Legacy symbol registration */
+static bool
+lua_config_register_symbol_legacy(lua_State *L, struct rspamd_config *cfg, int pos, int *id_out)
+{
+ const char *name = NULL, *type_str = NULL,
+ *description = NULL, *group = NULL;
+ double weight = 0, score = NAN, parent_float = NAN;
+ gboolean one_shot = FALSE;
+ int ret, cbref = -1;
+ unsigned int type = 0, flags = 0;
+ int64_t parent = 0, priority = 0, nshots = 0;
- if (!rspamd_symcache_add_symbol_augmentation(cfg->cache, ret,
- augentation_name, augmentation_value)) {
- lua_settop(L, prev_top);
- g_free(augmentation_value);
- g_free(augentation_name);
+ GError *err = NULL;
+ if (!rspamd_lua_parse_table_arguments(L, pos, &err,
+ RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
+ "name=S;weight=N;callback=F;type=S;priority=I;parent=D;"
+ "score=D;description=S;group=S;one_shot=B;nshots=I",
+ &name, &weight, &cbref, &type_str,
+ &priority, &parent_float,
+ &score, &description, &group, &one_shot, &nshots)) {
+ msg_err_config("bad arguments: %e", err);
+ g_error_free(err);
- return luaL_error(L, "unknown or invalid augmentation %s in symbol %s",
- augmentation, name);
- }
+ return false;
+ }
- g_free(augmentation_value);
- g_free(augentation_name);
- }
- else {
- /* Just a value */
- if (!rspamd_symcache_add_symbol_augmentation(cfg->cache, ret,
- augmentation, NULL)) {
- lua_settop(L, prev_top);
+ type = lua_parse_symbol_type(type_str);
- return luaL_error(L, "unknown augmentation %s in symbol %s",
- augmentation, name);
- }
- }
- }
+ if (!name && !(type & SYMBOL_TYPE_CALLBACK)) {
+ luaL_error(L, "no symbol name but type is not callback");
+
+ return false;
+ }
+ else if (!(type & SYMBOL_TYPE_VIRTUAL) && cbref == -1) {
+ luaL_error(L, "no callback for symbol %s", name);
+
+ return false;
+ }
+
+ if (isnan(parent_float)) {
+ parent = -1;
+ }
+ else {
+ parent = parent_float;
+ }
+
+ ret = rspamd_register_symbol_fromlua(L,
+ cfg,
+ name,
+ cbref,
+ weight == 0 ? 1.0 : weight,
+ priority,
+ type | flags,
+ parent,
+ NULL, NULL,
+ FALSE);
+
+ if (ret != -1) {
+ if (!isnan(score) || group) {
+ if (one_shot) {
+ nshots = 1;
+ }
+ if (nshots == 0) {
+ nshots = cfg->default_max_shots;
}
+
+ rspamd_config_add_symbol(cfg, name, score,
+ description, group, flags, 0, nshots);
}
+
+ *id_out = ret;
+
+ return true;
}
- else {
- lua_settop(L, prev_top);
- return luaL_error(L, "invalid arguments");
+ return false;
+}
+
+static int
+lua_config_register_symbol(lua_State *L)
+{
+ LUA_TRACE_POINT;
+ struct rspamd_config *cfg = lua_check_config(L, 1);
+ int id = -1;
+
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ if (lua_config_register_symbol_legacy(L, cfg, 2, &id)) {
+ lua_pushinteger(L, id);
+
+ return 1;
+ }
+ else {
+ return luaL_error(L, "bad arguments");
+ }
}
+ else if (lua_config_register_symbol_from_table(L, cfg, NULL, 2, &id)) {
+ lua_pushinteger(L, id);
- lua_settop(L, prev_top);
- lua_pushinteger(L, ret);
+ return 1;
+ }
- return 1;
+ return 0;
}
static int
@@ -2655,10 +2903,7 @@ lua_config_newindex(lua_State *L)
LUA_TRACE_POINT;
struct rspamd_config *cfg = lua_check_config(L, 1);
const char *name;
- GArray *allowed_ids = NULL, *forbidden_ids = NULL;
- int id, nshots;
- unsigned int flags = 0;
- gboolean optional = FALSE;
+ int id = -1;
name = luaL_checkstring(L, 2);
@@ -2679,291 +2924,14 @@ lua_config_newindex(lua_State *L)
FALSE);
}
else if (lua_type(L, 3) == LUA_TTABLE) {
- unsigned int type = SYMBOL_TYPE_NORMAL, priority = 0;
- int idx;
- double weight = 1.0, score = NAN;
- const char *type_str, *group = NULL, *description = NULL;
-
- /*
- * Table can have the following attributes:
- * "callback" - should be a callback function
- * "weight" - optional weight
- * "priority" - optional priority
- * "type" - optional type (normal, virtual, callback)
- * "flags" - optional flags
- * -- Metric options
- * "score" - optional default score (overridden by metric)
- * "group" - optional default group
- * "one_shot" - optional one shot mode
- * "description" - optional description
- */
- lua_pushvalue(L, 3);
- lua_pushstring(L, "callback");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) != LUA_TFUNCTION) {
- lua_pop(L, 2);
- msg_info_config("cannot find callback definition for %s",
- name);
- return 0;
- }
- idx = luaL_ref(L, LUA_REGISTRYINDEX);
-
- /* Optional fields */
- lua_pushstring(L, "weight");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TNUMBER) {
- weight = lua_tonumber(L, -1);
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "priority");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TNUMBER) {
- priority = lua_tointeger(L, -1);
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "optional");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TBOOLEAN) {
- optional = lua_toboolean(L, -1);
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "type");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TSTRING) {
- type_str = lua_tostring(L, -1);
- type = lua_parse_symbol_type(type_str);
- }
- lua_pop(L, 1);
-
- /* Deal with flags and ids */
- lua_pushstring(L, "flags");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- flags = lua_parse_symbol_flags(lua_tostring(L, -1));
- }
- else if (lua_type(L, -1) == LUA_TTABLE) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- flags |= lua_parse_symbol_flags(lua_tostring(L, -1));
- }
- }
- lua_pop(L, 1); /* Clean flags */
-
- lua_pushstring(L, "allowed_ids");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- allowed_ids = rspamd_process_id_list(lua_tostring(L, -1));
- }
- else if (lua_type(L, -1) == LUA_TTABLE) {
- allowed_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
- rspamd_lua_table_size(L, -1));
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- uint32_t v = lua_tointeger(L, -1);
- g_array_append_val(allowed_ids, v);
- }
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "forbidden_ids");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- forbidden_ids = rspamd_process_id_list(lua_tostring(L, -1));
- }
- else if (lua_type(L, -1) == LUA_TTABLE) {
- forbidden_ids = g_array_sized_new(FALSE, FALSE, sizeof(uint32_t),
- rspamd_lua_table_size(L, -1));
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- uint32_t v = lua_tointeger(L, -1);
- g_array_append_val(forbidden_ids, v);
- }
- }
- lua_pop(L, 1);
-
- id = rspamd_register_symbol_fromlua(L,
- cfg,
- name,
- idx,
- weight,
- priority,
- type | flags,
- -1,
- allowed_ids, forbidden_ids,
- optional);
-
- if (allowed_ids) {
- g_array_free(allowed_ids, TRUE);
- }
-
- if (forbidden_ids) {
- g_array_free(forbidden_ids, TRUE);
- }
-
- if (id != -1) {
- /* Check for condition */
- lua_pushstring(L, "condition");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TFUNCTION) {
- int condref;
-
- /* Here we pop function from the stack, so no lua_pop is required */
- condref = luaL_ref(L, LUA_REGISTRYINDEX);
- g_assert(name != NULL);
- rspamd_symcache_add_condition_delayed(cfg->cache,
- name, L, condref);
- }
- else {
- lua_pop(L, 1);
- }
-
- /* Check for augmentations */
- lua_pushstring(L, "augmentations");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TTABLE) {
-
- int tbl_idx = lua_gettop(L);
- for (lua_pushnil(L); lua_next(L, tbl_idx); lua_pop(L, 1)) {
- rspamd_symcache_add_symbol_augmentation(cfg->cache, id,
- lua_tostring(L, -1), NULL);
- }
- }
-
- lua_pop(L, 1);
- }
-
- /*
- * Now check if a symbol has not been registered in any metric and
- * insert default value if applicable
- */
- struct rspamd_symbol *sym = g_hash_table_lookup(cfg->symbols, name);
- if (sym == NULL || (sym->flags & RSPAMD_SYMBOL_FLAG_UNSCORED)) {
- nshots = cfg->default_max_shots;
-
- lua_pushstring(L, "score");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TNUMBER) {
- score = lua_tonumber(L, -1);
-
- if (sym) {
- /* Reset unscored flag */
- sym->flags &= ~RSPAMD_SYMBOL_FLAG_UNSCORED;
- }
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "group");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- group = lua_tostring(L, -1);
- }
- lua_pop(L, 1);
-
- if (!isnan(score) || group != NULL) {
- lua_pushstring(L, "description");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TSTRING) {
- description = lua_tostring(L, -1);
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "one_shot");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TBOOLEAN) {
- if (lua_toboolean(L, -1)) {
- nshots = 1;
- }
- }
- lua_pop(L, 1);
-
- lua_pushstring(L, "one_param");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TBOOLEAN) {
- if (lua_toboolean(L, -1)) {
- flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM;
- }
- }
- lua_pop(L, 1);
-
- /*
- * Do not override the existing symbols (using zero priority),
- * since we are defining default values here
- */
- if (!isnan(score)) {
- rspamd_config_add_symbol(cfg, name, score,
- description, group, flags, 0, nshots);
- }
- else if (group) {
- /* Add with zero score */
- rspamd_config_add_symbol(cfg, name, NAN,
- description, group, flags, 0, nshots);
- }
-
- lua_pushstring(L, "groups");
- lua_gettable(L, -2);
-
- if (lua_istable(L, -1)) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- if (lua_isstring(L, -1)) {
- rspamd_config_add_symbol_group(cfg, name,
- lua_tostring(L, -1));
- }
- else {
- return luaL_error(L, "invalid groups element");
- }
- }
- }
-
- lua_pop(L, 1);
- }
- }
- else {
- /* Fill in missing fields from lua definition if they are not set */
- if (sym->description == NULL) {
- lua_pushstring(L, "description");
- lua_gettable(L, -2);
-
- if (lua_type(L, -1) == LUA_TSTRING) {
- description = lua_tostring(L, -1);
- }
- lua_pop(L, 1);
-
- if (description) {
- sym->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
- }
- }
-
- /* If ungrouped and there is a group defined in lua, change the primary group
- * Otherwise, add to the list of groups for this symbol. */
- lua_pushstring(L, "group");
- lua_gettable(L, -2);
- if (lua_type(L, -1) == LUA_TSTRING) {
- group = lua_tostring(L, -1);
- }
- lua_pop(L, 1);
- if (group) {
- if (sym->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED) {
- /* Unset the "ungrouped" group */
- sym->gr = NULL;
- }
- /* Add the group. If the symbol was ungrouped, this will
- * clear RSPAMD_SYMBOL_FLAG_UNGROUPED from the flags. */
- rspamd_config_add_symbol_group(cfg, name, group);
- }
+ /* Table symbol */
+ if (lua_config_register_symbol_from_table(L, cfg, name, 3, &id)) {
+ lua_pushinteger(L, id);
+ return 1;
}
-
- /* Remove table from stack */
- lua_pop(L, 1);
+ }
+ else {
+ return luaL_error(L, "invalid value for symbol");
}
}
else {
@@ -3941,6 +3909,8 @@ lua_config_get_groups(lua_State *L)
lua_setfield(L, -2, "description");
lua_pushnumber(L, gr->max_score);
lua_setfield(L, -2, "max_score");
+ lua_pushnumber(L, gr->min_score);
+ lua_setfield(L, -2, "min_score");
lua_pushboolean(L, (gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC) != 0);
lua_setfield(L, -2, "is_public");
/* TODO: maybe push symbols as well */
diff --git a/src/lua/lua_cryptobox.c b/src/lua/lua_cryptobox.c
index bad7d7024..1b9074f58 100644
--- a/src/lua/lua_cryptobox.c
+++ b/src/lua/lua_cryptobox.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -257,7 +257,6 @@ lua_check_cryptobox_secretbox(lua_State *L, int pos)
* Loads public key from base32 encoded file
* @param {string} file filename to load
* @param {string} type optional 'sign' or 'kex' for signing and encryption
- * @param {string} alg optional 'default' or 'nist' for curve25519/nistp256 keys
* @return {cryptobox_pubkey} new public key
*/
static int
@@ -267,7 +266,6 @@ lua_cryptobox_pubkey_load(lua_State *L)
struct rspamd_cryptobox_pubkey *pkey = NULL, **ppkey;
const char *filename, *arg;
int type = RSPAMD_KEYPAIR_SIGN;
- int alg = RSPAMD_CRYPTOBOX_MODE_25519;
unsigned char *map;
gsize len;
@@ -293,19 +291,8 @@ lua_cryptobox_pubkey_load(lua_State *L)
type = RSPAMD_KEYPAIR_KEX;
}
}
- if (lua_type(L, 3) == LUA_TSTRING) {
- /* algorithm */
- arg = lua_tostring(L, 3);
-
- if (strcmp(arg, "default") == 0 || strcmp(arg, "curve25519") == 0) {
- type = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else if (strcmp(arg, "nist") == 0) {
- type = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- }
- pkey = rspamd_pubkey_from_base32(map, len, type, alg);
+ pkey = rspamd_pubkey_from_base32(map, len, type);
if (pkey == NULL) {
msg_err("cannot open pubkey from file: %s", filename);
@@ -333,7 +320,6 @@ lua_cryptobox_pubkey_load(lua_State *L)
* Loads public key from base32 encoded string
* @param {base32 string} base32 string with the key
* @param {string} type optional 'sign' or 'kex' for signing and encryption
- * @param {string} alg optional 'default' or 'nist' for curve25519/nistp256 keys
* @return {cryptobox_pubkey} new public key
*/
static int
@@ -344,7 +330,6 @@ lua_cryptobox_pubkey_create(lua_State *L)
const char *buf, *arg;
gsize len;
int type = RSPAMD_KEYPAIR_SIGN;
- int alg = RSPAMD_CRYPTOBOX_MODE_25519;
buf = luaL_checklstring(L, 1, &len);
if (buf != NULL) {
@@ -359,19 +344,8 @@ lua_cryptobox_pubkey_create(lua_State *L)
type = RSPAMD_KEYPAIR_KEX;
}
}
- if (lua_type(L, 3) == LUA_TSTRING) {
- /* algorithm */
- arg = lua_tostring(L, 3);
-
- if (strcmp(arg, "default") == 0 || strcmp(arg, "curve25519") == 0) {
- type = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else if (strcmp(arg, "nist") == 0) {
- type = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- }
- pkey = rspamd_pubkey_from_base32(buf, len, type, alg);
+ pkey = rspamd_pubkey_from_base32(buf, len, type);
if (pkey == NULL) {
msg_err("cannot load pubkey from string");
@@ -477,7 +451,6 @@ lua_cryptobox_keypair_load(lua_State *L)
* @function rspamd_cryptobox_keypair.create([type='encryption'[, alg='curve25519']])
* Generates new keypair
* @param {string} type type of keypair: 'encryption' (default) or 'sign'
- * @param {string} alg algorithm of keypair: 'curve25519' (default) or 'nist'
* @return {cryptobox_keypair} new keypair
*/
static int
@@ -486,7 +459,6 @@ lua_cryptobox_keypair_create(lua_State *L)
LUA_TRACE_POINT;
struct rspamd_cryptobox_keypair *kp, **pkp;
enum rspamd_cryptobox_keypair_type type = RSPAMD_KEYPAIR_KEX;
- enum rspamd_cryptobox_mode alg = RSPAMD_CRYPTOBOX_MODE_25519;
if (lua_isstring(L, 1)) {
const char *str = lua_tostring(L, 1);
@@ -502,21 +474,7 @@ lua_cryptobox_keypair_create(lua_State *L)
}
}
- if (lua_isstring(L, 2)) {
- const char *str = lua_tostring(L, 2);
-
- if (strcmp(str, "nist") == 0 || strcmp(str, "openssl") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- else if (strcmp(str, "curve25519") == 0 || strcmp(str, "default") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else {
- return luaL_error(L, "invalid keypair algorithm: %s", str);
- }
- }
-
- kp = rspamd_keypair_new(type, alg);
+ kp = rspamd_keypair_new(type);
pkp = lua_newuserdata(L, sizeof(gpointer));
*pkp = kp;
@@ -606,12 +564,7 @@ lua_cryptobox_keypair_get_alg(lua_State *L)
struct rspamd_cryptobox_keypair *kp = lua_check_cryptobox_keypair(L, 1);
if (kp) {
- if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) {
- lua_pushstring(L, "curve25519");
- }
- else {
- lua_pushstring(L, "nist");
- }
+ lua_pushstring(L, "curve25519");
}
else {
return luaL_error(L, "invalid arguments");
@@ -636,7 +589,7 @@ lua_cryptobox_keypair_get_pk(lua_State *L)
if (kp) {
data = rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, &dlen);
- pk = rspamd_pubkey_from_bin(data, dlen, kp->type, kp->alg);
+ pk = rspamd_pubkey_from_bin(data, dlen, kp->type);
if (pk == NULL) {
return luaL_error(L, "invalid keypair");
@@ -654,7 +607,7 @@ lua_cryptobox_keypair_get_pk(lua_State *L)
}
/***
- * @function rspamd_cryptobox_signature.load(file, [alg = 'curve25519'])
+ * @function rspamd_cryptobox_signature.load(file)
* Loads signature from raw file
* @param {string} file filename to load
* @return {cryptobox_signature} new signature
@@ -668,7 +621,6 @@ lua_cryptobox_signature_load(lua_State *L)
gpointer data;
int fd;
struct stat st;
- enum rspamd_cryptobox_mode alg = RSPAMD_CRYPTOBOX_MODE_25519;
filename = luaL_checkstring(L, 1);
if (filename != NULL) {
@@ -686,22 +638,6 @@ lua_cryptobox_signature_load(lua_State *L)
lua_pushnil(L);
}
else {
- if (lua_isstring(L, 2)) {
- const char *str = lua_tostring(L, 2);
-
- if (strcmp(str, "nist") == 0 || strcmp(str, "openssl") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- else if (strcmp(str, "curve25519") == 0 || strcmp(str, "default") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else {
- munmap(data, st.st_size);
- close(fd);
-
- return luaL_error(L, "invalid keypair algorithm: %s", str);
- }
- }
if (st.st_size > 0) {
sig = rspamd_fstring_new_init(data, st.st_size);
psig = lua_newuserdata(L, sizeof(rspamd_fstring_t *));
@@ -711,7 +647,7 @@ lua_cryptobox_signature_load(lua_State *L)
else {
msg_err("size of %s mismatches: %d while %d is expected",
filename, (int) st.st_size,
- rspamd_cryptobox_signature_bytes(alg));
+ crypto_sign_bytes());
lua_pushnil(L);
}
@@ -821,7 +757,7 @@ lua_cryptobox_signature_create(lua_State *L)
}
if (data != NULL) {
- if (dlen == rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519)) {
+ if (dlen == crypto_sign_bytes()) {
sig = rspamd_fstring_new_init(data, dlen);
psig = lua_newuserdata(L, sizeof(rspamd_fstring_t *));
rspamd_lua_setclass(L, rspamd_cryptobox_signature_classname, -1);
@@ -1723,7 +1659,7 @@ lua_cryptobox_hash_gc(lua_State *L)
}
/***
- * @function rspamd_cryptobox.verify_memory(pk, sig, data, [alg = 'curve25519'])
+ * @function rspamd_cryptobox.verify_memory(pk, sig, data)
* Check memory using specified cryptobox key and signature
* @param {pubkey} pk public key to verify
* @param {sig} signature to check
@@ -1738,7 +1674,6 @@ lua_cryptobox_verify_memory(lua_State *L)
rspamd_fstring_t *signature;
struct rspamd_lua_text *t;
const char *data;
- enum rspamd_cryptobox_mode alg = RSPAMD_CRYPTOBOX_MODE_25519;
gsize len;
int ret;
@@ -1759,23 +1694,9 @@ lua_cryptobox_verify_memory(lua_State *L)
data = luaL_checklstring(L, 3, &len);
}
- if (lua_isstring(L, 4)) {
- const char *str = lua_tostring(L, 4);
-
- if (strcmp(str, "nist") == 0 || strcmp(str, "openssl") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- else if (strcmp(str, "curve25519") == 0 || strcmp(str, "default") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else {
- return luaL_error(L, "invalid algorithm: %s", str);
- }
- }
-
if (pk != NULL && signature != NULL && data != NULL) {
ret = rspamd_cryptobox_verify(signature->str, signature->len, data, len,
- rspamd_pubkey_get_pk(pk, NULL), alg);
+ rspamd_pubkey_get_pk(pk, NULL));
if (ret) {
lua_pushboolean(L, 1);
@@ -1792,7 +1713,7 @@ lua_cryptobox_verify_memory(lua_State *L)
}
/***
- * @function rspamd_cryptobox.verify_file(pk, sig, file, [alg = 'curve25519'])
+ * @function rspamd_cryptobox.verify_file(pk, sig, file)
* Check file using specified cryptobox key and signature
* @param {pubkey} pk public key to verify
* @param {sig} signature to check
@@ -1807,7 +1728,6 @@ lua_cryptobox_verify_file(lua_State *L)
struct rspamd_cryptobox_pubkey *pk;
rspamd_fstring_t *signature;
unsigned char *map = NULL;
- enum rspamd_cryptobox_mode alg = RSPAMD_CRYPTOBOX_MODE_25519;
gsize len;
int ret;
@@ -1815,26 +1735,12 @@ lua_cryptobox_verify_file(lua_State *L)
signature = lua_check_cryptobox_sign(L, 2);
fname = luaL_checkstring(L, 3);
- if (lua_isstring(L, 4)) {
- const char *str = lua_tostring(L, 4);
-
- if (strcmp(str, "nist") == 0 || strcmp(str, "openssl") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
- else if (strcmp(str, "curve25519") == 0 || strcmp(str, "default") == 0) {
- alg = RSPAMD_CRYPTOBOX_MODE_25519;
- }
- else {
- return luaL_error(L, "invalid algorithm: %s", str);
- }
- }
-
map = rspamd_file_xmap(fname, PROT_READ, &len, TRUE);
if (map != NULL && pk != NULL && signature != NULL) {
ret = rspamd_cryptobox_verify(signature->str, signature->len,
map, len,
- rspamd_pubkey_get_pk(pk, NULL), alg);
+ rspamd_pubkey_get_pk(pk, NULL));
if (ret) {
lua_pushboolean(L, 1);
@@ -1896,12 +1802,11 @@ lua_cryptobox_sign_memory(lua_State *L)
return luaL_error(L, "invalid arguments");
}
- sig = rspamd_fstring_sized_new(rspamd_cryptobox_signature_bytes(
- rspamd_keypair_alg(kp)));
+ sig = rspamd_fstring_sized_new(crypto_sign_bytes());
unsigned long long siglen = sig->len;
rspamd_cryptobox_sign(sig->str, &siglen, data,
- len, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), rspamd_keypair_alg(kp));
+ len, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL));
sig->len = siglen;
psig = lua_newuserdata(L, sizeof(void *));
@@ -1942,13 +1847,12 @@ lua_cryptobox_sign_file(lua_State *L)
lua_pushnil(L);
}
else {
- sig = rspamd_fstring_sized_new(rspamd_cryptobox_signature_bytes(
- rspamd_keypair_alg(kp)));
+ sig = rspamd_fstring_sized_new(crypto_sign_bytes());
unsigned long long siglen = sig->len;
rspamd_cryptobox_sign(sig->str, &siglen, data,
- len, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), rspamd_keypair_alg(kp));
+ len, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL));
sig->len = siglen;
psig = lua_newuserdata(L, sizeof(void *));
@@ -1961,7 +1865,7 @@ lua_cryptobox_sign_file(lua_State *L)
}
/***
- * @function rspamd_cryptobox.encrypt_memory(kp, data[, nist=false])
+ * @function rspamd_cryptobox.encrypt_memory(kp, data)
* Encrypt data using specified keypair/pubkey
* @param {keypair|string} kp keypair or pubkey in base32 to use
* @param {string|text} data
@@ -1993,8 +1897,7 @@ lua_cryptobox_encrypt_memory(lua_State *L)
gsize blen;
b32 = lua_tolstring(L, 1, &blen);
- pk = rspamd_pubkey_from_base32(b32, blen, RSPAMD_KEYPAIR_KEX,
- lua_toboolean(L, 3) ? RSPAMD_CRYPTOBOX_MODE_NIST : RSPAMD_CRYPTOBOX_MODE_25519);
+ pk = rspamd_pubkey_from_base32(b32, blen, RSPAMD_KEYPAIR_KEX);
owned_pk = true;
}
@@ -2063,7 +1966,7 @@ err:
}
/***
- * @function rspamd_cryptobox.encrypt_file(kp|pk_string, filename[, nist=false])
+ * @function rspamd_cryptobox.encrypt_file(kp|pk_string, filename)
* Encrypt data using specified keypair/pubkey
* @param {keypair|string} kp keypair or pubkey in base32 to use
* @param {string} filename
@@ -2096,8 +1999,7 @@ lua_cryptobox_encrypt_file(lua_State *L)
gsize blen;
b32 = lua_tolstring(L, 1, &blen);
- pk = rspamd_pubkey_from_base32(b32, blen, RSPAMD_KEYPAIR_KEX,
- lua_toboolean(L, 3) ? RSPAMD_CRYPTOBOX_MODE_NIST : RSPAMD_CRYPTOBOX_MODE_25519);
+ pk = rspamd_pubkey_from_base32(b32, blen, RSPAMD_KEYPAIR_KEX);
own_pk = true;
}
@@ -2658,11 +2560,11 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L)
char *b64_data;
gsize b64_len;
- rspamd_cryptobox_keypair_sig(pk, sk, RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_keypair_sig(pk, sk);
/* Process private key */
b64_data = rspamd_encode_base64(sk,
- rspamd_cryptobox_sk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519),
+ crypto_sign_secretkeybytes(),
-1, &b64_len);
priv_out = lua_newuserdata(L, sizeof(*priv_out));
@@ -2673,7 +2575,7 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L)
/* Process public key */
b64_data = rspamd_encode_base64(pk,
- rspamd_cryptobox_pk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519),
+ crypto_sign_publickeybytes(),
-1, &b64_len);
pub_out = lua_newuserdata(L, sizeof(*pub_out));
@@ -2691,7 +2593,7 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L)
char *b64_data;
gsize b64_len;
- rspamd_cryptobox_keypair_sig(pk, sk, RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_cryptobox_keypair_sig(pk, sk);
/* Process private key */
b64_data = rspamd_encode_base64(sk,
@@ -2706,7 +2608,7 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L)
/* Process public key */
b64_data = rspamd_encode_base64(pk,
- rspamd_cryptobox_pk_sig_bytes(RSPAMD_CRYPTOBOX_MODE_25519),
+ crypto_sign_publickeybytes(),
-1, &b64_len);
pub_out = lua_newuserdata(L, sizeof(*pub_out));
diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c
index 2032f7dc1..8ba612c1b 100644
--- a/src/lua/lua_http.c
+++ b/src/lua/lua_http.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Vsevolod Stakhov
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -907,8 +907,7 @@ lua_http_request(lua_State *L)
gsize inlen;
in = lua_tolstring(L, -1, &inlen);
- peer_key = rspamd_pubkey_from_base32(in, inlen,
- RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
+ peer_key = rspamd_pubkey_from_base32(in, inlen, RSPAMD_KEYPAIR_KEX);
}
lua_pop(L, 1);
diff --git a/src/lua/lua_map.c b/src/lua/lua_map.c
index cce78ff2c..1cc2ce1bd 100644
--- a/src/lua/lua_map.c
+++ b/src/lua/lua_map.c
@@ -1293,8 +1293,7 @@ lua_map_set_sign_key(lua_State *L)
pk_str = lua_tolstring(L, 2, &len);
if (map && pk_str) {
- pk = rspamd_pubkey_from_base32(pk_str, len, RSPAMD_KEYPAIR_SIGN,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ pk = rspamd_pubkey_from_base32(pk_str, len, RSPAMD_KEYPAIR_SIGN);
if (!pk) {
return luaL_error(L, "invalid pubkey string");
diff --git a/src/lua/lua_redis.c b/src/lua/lua_redis.c
index f95abb577..d20c496ed 100644
--- a/src/lua/lua_redis.c
+++ b/src/lua/lua_redis.c
@@ -104,7 +104,7 @@ struct lua_redis_userdata {
char *server;
char log_tag[RSPAMD_LOG_ID_LEN + 1];
struct lua_redis_request_specific_userdata *specific;
- double timeout;
+ ev_tstamp timeout;
uint16_t port;
uint16_t terminated;
};
@@ -280,16 +280,23 @@ lua_redis_fin(void *arg)
* @param code
* @param ud
*/
+#ifdef __GNUC__
+__attribute__((format(printf, 1, 5)))
+#endif
static void
lua_redis_push_error(const char *err,
struct lua_redis_ctx *ctx,
struct lua_redis_request_specific_userdata *sp_ud,
- gboolean connected)
+ gboolean connected,
+ ...)
{
struct lua_redis_userdata *ud = sp_ud->c;
struct lua_callback_state cbs;
lua_State *L;
+ va_list ap;
+ va_start(ap, connected);
+
if (!(sp_ud->flags & (LUA_REDIS_SPECIFIC_REPLIED | LUA_REDIS_SPECIFIC_FINISHED))) {
if (sp_ud->cbref != -1) {
@@ -302,7 +309,7 @@ lua_redis_push_error(const char *err,
lua_rawgeti(cbs.L, LUA_REGISTRYINDEX, sp_ud->cbref);
/* String of error */
- lua_pushstring(cbs.L, err);
+ lua_pushvfstring(cbs.L, err, ap);
/* Data is nil */
lua_pushnil(cbs.L);
@@ -331,6 +338,8 @@ lua_redis_push_error(const char *err,
lua_redis_fin(sp_ud);
}
}
+
+ va_end(ap);
}
static void
@@ -479,7 +488,7 @@ lua_redis_callback(redisAsyncContext *c, gpointer r, gpointer priv)
lua_redis_push_data(reply, ctx, sp_ud);
}
else {
- lua_redis_push_error(reply->str, ctx, sp_ud, TRUE);
+ lua_redis_push_error("%s", ctx, sp_ud, TRUE, reply->str);
}
}
else {
@@ -488,10 +497,10 @@ lua_redis_callback(redisAsyncContext *c, gpointer r, gpointer priv)
}
else {
if (c->err == REDIS_ERR_IO) {
- lua_redis_push_error(strerror(errno), ctx, sp_ud, TRUE);
+ lua_redis_push_error("%s", ctx, sp_ud, TRUE, strerror(errno));
}
else {
- lua_redis_push_error(c->errstr, ctx, sp_ud, TRUE);
+ lua_redis_push_error("%s", ctx, sp_ud, TRUE, c->errstr);
}
}
}
@@ -750,7 +759,7 @@ lua_redis_timeout(EV_P_ ev_timer *w, int revents)
REDIS_RETAIN(ctx);
msg_debug_lua_redis("timeout while querying redis server: %p, redis: %p", sp_ud,
sp_ud->c->ctx);
- lua_redis_push_error("timeout while connecting the server", ctx, sp_ud, TRUE);
+ lua_redis_push_error("timeout while connecting the server (%.2f sec)", ctx, sp_ud, TRUE, ud->timeout);
if (sp_ud->c->ctx) {
ac = sp_ud->c->ctx;
diff --git a/src/lua/lua_rsa.c b/src/lua/lua_rsa.c
index 0f67e91d1..0c56b223b 100644
--- a/src/lua/lua_rsa.c
+++ b/src/lua/lua_rsa.c
@@ -91,22 +91,22 @@ static const struct luaL_reg rsasignlib_m[] = {
{"__gc", lua_rsa_signature_gc},
{NULL, NULL}};
-static RSA *
+static EVP_PKEY *
lua_check_rsa_pubkey(lua_State *L, int pos)
{
void *ud = rspamd_lua_check_udata(L, pos, rspamd_rsa_pubkey_classname);
luaL_argcheck(L, ud != NULL, 1, "'rsa_pubkey' expected");
- return ud ? *((RSA **) ud) : NULL;
+ return ud ? *((EVP_PKEY **) ud) : NULL;
}
-static RSA *
+static EVP_PKEY *
lua_check_rsa_privkey(lua_State *L, int pos)
{
void *ud = rspamd_lua_check_udata(L, pos, rspamd_rsa_privkey_classname);
luaL_argcheck(L, ud != NULL, 1, "'rsa_privkey' expected");
- return ud ? *((RSA **) ud) : NULL;
+ return ud ? *((EVP_PKEY **) ud) : NULL;
}
static rspamd_fstring_t *
@@ -121,7 +121,7 @@ lua_check_rsa_sign(lua_State *L, int pos)
static int
lua_rsa_pubkey_load(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
const char *filename;
FILE *f;
@@ -135,15 +135,15 @@ lua_rsa_pubkey_load(lua_State *L)
lua_pushnil(L);
}
else {
- if (!PEM_read_RSA_PUBKEY(f, &rsa, NULL, NULL)) {
+ if (!PEM_read_PUBKEY(f, &pkey, NULL, NULL)) {
msg_err("cannot open pubkey from file: %s, %s", filename,
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_pubkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
fclose(f);
}
@@ -161,15 +161,14 @@ lua_rsa_privkey_save(lua_State *L)
const char *type = "pem";
FILE *f;
int ret;
-
- RSA *rsa = lua_check_rsa_privkey(L, 1);
+ EVP_PKEY *pkey = lua_check_rsa_privkey(L, 1);
filename = luaL_checkstring(L, 2);
if (lua_gettop(L) > 2) {
type = luaL_checkstring(L, 3);
}
- if (rsa != NULL && filename != NULL) {
+ if (pkey != NULL && filename != NULL) {
if (strcmp(filename, "-") == 0) {
f = stdout;
}
@@ -189,10 +188,10 @@ lua_rsa_privkey_save(lua_State *L)
}
if (strcmp(type, "der") == 0) {
- ret = i2d_RSAPrivateKey_fp(f, rsa);
+ ret = i2d_PrivateKey_fp(f, pkey);
}
else {
- ret = PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL, NULL);
+ ret = PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL);
}
if (!ret) {
@@ -223,7 +222,7 @@ lua_rsa_privkey_save(lua_State *L)
static int
lua_rsa_pubkey_create(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey, **ppkey;
const char *buf;
BIO *bp;
@@ -231,15 +230,15 @@ lua_rsa_pubkey_create(lua_State *L)
if (buf != NULL) {
bp = BIO_new_mem_buf((void *) buf, -1);
- if (!PEM_read_bio_RSA_PUBKEY(bp, &rsa, NULL, NULL)) {
+ if (!PEM_read_bio_PUBKEY(bp, &pkey, NULL, NULL)) {
msg_err("cannot parse pubkey: %s",
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_pubkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
BIO_free(bp);
}
@@ -252,10 +251,10 @@ lua_rsa_pubkey_create(lua_State *L)
static int
lua_rsa_pubkey_gc(lua_State *L)
{
- RSA *rsa = lua_check_rsa_pubkey(L, 1);
+ EVP_PKEY *pkey = lua_check_rsa_pubkey(L, 1);
- if (rsa != NULL) {
- RSA_free(rsa);
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
}
return 0;
@@ -264,18 +263,18 @@ lua_rsa_pubkey_gc(lua_State *L)
static int
lua_rsa_pubkey_tostring(lua_State *L)
{
- RSA *rsa = lua_check_rsa_pubkey(L, 1);
+ EVP_PKEY *pkey = lua_check_rsa_pubkey(L, 1);
- if (rsa != NULL) {
+ if (pkey != NULL) {
BIO *pubout = BIO_new(BIO_s_mem());
const char *pubdata;
gsize publen;
- int rc = i2d_RSA_PUBKEY_bio(pubout, rsa);
+ int rc = i2d_PUBKEY_bio(pubout, pkey);
if (rc != 1) {
BIO_free(pubout);
- return luaL_error(L, "i2d_RSA_PUBKEY_bio failed");
+ return luaL_error(L, "i2d_PUBKEY_bio failed");
}
publen = BIO_get_mem_data(pubout, &pubdata);
@@ -292,7 +291,7 @@ lua_rsa_pubkey_tostring(lua_State *L)
static int
lua_rsa_privkey_load_file(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
const char *filename;
FILE *f;
@@ -306,15 +305,15 @@ lua_rsa_privkey_load_file(lua_State *L)
lua_pushnil(L);
}
else {
- if (!PEM_read_RSAPrivateKey(f, &rsa, NULL, NULL)) {
+ if (!PEM_read_PrivateKey(f, &pkey, NULL, NULL)) {
msg_err("cannot open private key from file: %s, %s", filename,
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
fclose(f);
}
@@ -328,7 +327,7 @@ lua_rsa_privkey_load_file(lua_State *L)
static int
lua_rsa_privkey_load_pem(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
BIO *b;
struct rspamd_lua_text *t;
const char *data;
@@ -351,15 +350,15 @@ lua_rsa_privkey_load_pem(lua_State *L)
if (data != NULL) {
b = BIO_new_mem_buf(data, len);
- if (!PEM_read_bio_RSAPrivateKey(b, &rsa, NULL, NULL)) {
+ if (!PEM_read_bio_PrivateKey(b, &pkey, NULL, NULL)) {
msg_err("cannot open private key from data, %s",
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
BIO_free(b);
@@ -374,7 +373,7 @@ lua_rsa_privkey_load_pem(lua_State *L)
static int
lua_rsa_privkey_load_raw(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
BIO *b;
struct rspamd_lua_text *t;
const char *data;
@@ -396,17 +395,17 @@ lua_rsa_privkey_load_raw(lua_State *L)
if (data != NULL) {
b = BIO_new_mem_buf(data, len);
- rsa = d2i_RSAPrivateKey_bio(b, NULL);
+ pkey = d2i_PrivateKey_bio(b, NULL);
- if (rsa == NULL) {
+ if (pkey == NULL) {
msg_err("cannot open private key from data, %s",
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
BIO_free(b);
@@ -421,9 +420,8 @@ lua_rsa_privkey_load_raw(lua_State *L)
static int
lua_rsa_privkey_load_base64(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
BIO *b;
- EVP_PKEY *evp = NULL;
struct rspamd_lua_text *t;
const char *data;
unsigned char *decoded;
@@ -454,21 +452,18 @@ lua_rsa_privkey_load_base64(lua_State *L)
b = BIO_new_mem_buf(decoded, dec_len);
- if (d2i_PrivateKey_bio(b, &evp) != NULL) {
- rsa = EVP_PKEY_get1_RSA(evp);
-
- if (rsa == NULL) {
+ if (d2i_PrivateKey_bio(b, &pkey) != NULL) {
+ if (pkey == NULL) {
msg_err("cannot open RSA private key from data, %s",
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
- EVP_PKEY_free(evp);
}
else {
msg_err("cannot open EVP private key from data, %s",
@@ -489,7 +484,7 @@ lua_rsa_privkey_load_base64(lua_State *L)
static int
lua_rsa_privkey_create(lua_State *L)
{
- RSA *rsa = NULL, **prsa;
+ EVP_PKEY *pkey = NULL, **ppkey;
const char *buf;
BIO *bp;
@@ -497,15 +492,15 @@ lua_rsa_privkey_create(lua_State *L)
if (buf != NULL) {
bp = BIO_new_mem_buf((void *) buf, -1);
- if (!PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, NULL)) {
+ if (!PEM_read_bio_PrivateKey(bp, &pkey, NULL, NULL)) {
msg_err("cannot parse private key: %s",
ERR_error_string(ERR_get_error(), NULL));
lua_pushnil(L);
}
else {
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = rsa;
+ *ppkey = pkey;
}
BIO_free(bp);
}
@@ -518,10 +513,10 @@ lua_rsa_privkey_create(lua_State *L)
static int
lua_rsa_privkey_gc(lua_State *L)
{
- RSA *rsa = lua_check_rsa_privkey(L, 1);
+ EVP_PKEY *pkey = lua_check_rsa_privkey(L, 1);
- if (rsa != NULL) {
- RSA_free(rsa);
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
}
return 0;
@@ -699,19 +694,22 @@ lua_rsa_signature_base64(lua_State *L)
static int
lua_rsa_verify_memory(lua_State *L)
{
- RSA *rsa;
+ EVP_PKEY *pkey;
rspamd_fstring_t *signature;
const char *data;
gsize sz;
int ret;
- rsa = lua_check_rsa_pubkey(L, 1);
+ pkey = lua_check_rsa_pubkey(L, 1);
signature = lua_check_rsa_sign(L, 2);
data = luaL_checklstring(L, 3, &sz);
- if (rsa != NULL && signature != NULL && data != NULL) {
- ret = RSA_verify(NID_sha256, data, sz,
- signature->str, signature->len, rsa);
+ if (pkey != NULL && signature != NULL && data != NULL) {
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL);
+ g_assert(pctx != NULL);
+ g_assert(EVP_PKEY_verify_init(pctx) == 1);
+
+ ret = EVP_PKEY_verify(pctx, signature->str, signature->len, data, sz);
if (ret == 0) {
lua_pushboolean(L, FALSE);
@@ -722,6 +720,7 @@ lua_rsa_verify_memory(lua_State *L)
else {
lua_pushboolean(L, TRUE);
}
+ EVP_PKEY_CTX_free(pctx);
}
else {
lua_pushnil(L);
@@ -743,22 +742,26 @@ lua_rsa_verify_memory(lua_State *L)
static int
lua_rsa_sign_memory(lua_State *L)
{
- RSA *rsa;
+ EVP_PKEY *pkey;
rspamd_fstring_t *signature, **psig;
const char *data;
gsize sz;
int ret;
- rsa = lua_check_rsa_privkey(L, 1);
+ pkey = lua_check_rsa_privkey(L, 1);
data = luaL_checklstring(L, 2, &sz);
- if (rsa != NULL && data != NULL) {
- signature = rspamd_fstring_sized_new(RSA_size(rsa));
+ if (pkey != NULL && data != NULL) {
+ signature = rspamd_fstring_sized_new(EVP_PKEY_get_size(pkey));
+
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL);
+ g_assert(pctx != NULL);
- unsigned int siglen = signature->len;
- ret = RSA_sign(NID_sha256, data, sz,
- signature->str, &siglen, rsa);
+ g_assert(EVP_PKEY_sign_init(pctx) == 1);
+ size_t slen = signature->allocated;
+ ret = EVP_PKEY_sign(pctx, signature->str, &slen, data, sz);
+ EVP_PKEY_CTX_free(pctx);
if (ret != 1) {
rspamd_fstring_free(signature);
@@ -766,7 +769,7 @@ lua_rsa_sign_memory(lua_State *L)
ERR_error_string(ERR_get_error(), NULL));
}
else {
- signature->len = siglen;
+ signature->len = slen;
psig = lua_newuserdata(L, sizeof(rspamd_fstring_t *));
rspamd_lua_setclass(L, rspamd_rsa_signature_classname, -1);
*psig = signature;
@@ -783,7 +786,7 @@ static int
lua_rsa_keypair(lua_State *L)
{
BIGNUM *e;
- RSA *rsa, *pub_rsa, *priv_rsa, **prsa;
+ EVP_PKEY *pkey = NULL, *pub_pkey, *priv_pkey, **ppkey;
int bits = lua_gettop(L) > 0 ? lua_tointeger(L, 1) : 1024;
if (bits > 4096 || bits < 512) {
@@ -791,21 +794,30 @@ lua_rsa_keypair(lua_State *L)
}
e = BN_new();
- rsa = RSA_new();
+
g_assert(BN_set_word(e, RSA_F4) == 1);
- g_assert(RSA_generate_key_ex(rsa, bits, e, NULL) == 1);
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ g_assert(pctx != NULL);
+ g_assert(EVP_PKEY_keygen_init(pctx) == 1);
+
+ g_assert(EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, bits) == 1);
+ g_assert(EVP_PKEY_CTX_set1_rsa_keygen_pubexp(pctx, e) == 1);
+
+ g_assert(EVP_PKEY_keygen(pctx, &pkey) == 1);
+ g_assert(pkey != NULL);
- priv_rsa = RSAPrivateKey_dup(rsa);
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ priv_pkey = EVP_PKEY_dup(pkey);
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1);
- *prsa = priv_rsa;
+ *ppkey = priv_pkey;
- pub_rsa = RSAPublicKey_dup(rsa);
- prsa = lua_newuserdata(L, sizeof(RSA *));
+ pub_pkey = EVP_PKEY_dup(pkey);
+ ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *));
rspamd_lua_setclass(L, rspamd_rsa_pubkey_classname, -1);
- *prsa = pub_rsa;
+ *ppkey = pub_pkey;
- RSA_free(rsa);
+ EVP_PKEY_free(pkey);
+ EVP_PKEY_CTX_free(pctx);
BN_free(e);
return 2;
diff --git a/src/lua/lua_spf.c b/src/lua/lua_spf.c
index 46e72202f..850ce2120 100644
--- a/src/lua/lua_spf.c
+++ b/src/lua/lua_spf.c
@@ -89,6 +89,8 @@ lua_load_spf(lua_State *L)
lua_setfield(L, -2, "perm_fail");
lua_pushinteger(L, RSPAMD_SPF_FLAG_CACHED);
lua_setfield(L, -2, "cached");
+ lua_pushinteger(L, RSPAMD_SPF_RESOLVED_PLUSALL);
+ lua_setfield(L, -2, "plusall");
lua_setfield(L, -2, "flags");
@@ -368,6 +370,11 @@ spf_check_element(lua_State *L, struct spf_resolved *rec, struct spf_addr *addr,
lua_pushinteger(L, RSPAMD_SPF_RESOLVED_TEMP_FAILED);
lua_pushfstring(L, "%cany", spf_mech_char(addr->mech));
}
+ else if (rec->flags & RSPAMD_SPF_RESOLVED_PLUSALL) {
+ lua_pushboolean(L, false);
+ lua_pushinteger(L, RSPAMD_SPF_RESOLVED_PLUSALL);
+ lua_pushfstring(L, "%cany", spf_mech_char(addr->mech));
+ }
else {
lua_pushboolean(L, true);
lua_pushinteger(L, addr->mech);
diff --git a/src/lua/lua_task.c b/src/lua/lua_task.c
index 21e24fc45..3968c01eb 100644
--- a/src/lua/lua_task.c
+++ b/src/lua/lua_task.c
@@ -1318,6 +1318,7 @@ static const struct luaL_reg tasklib_m[] = {
LUA_INTERFACE_DEF(task, get_metric_threshold),
LUA_INTERFACE_DEF(task, set_metric_score),
LUA_INTERFACE_DEF(task, set_metric_subject),
+ {"set_subject", lua_task_set_metric_subject},
LUA_INTERFACE_DEF(task, learn),
LUA_INTERFACE_DEF(task, set_settings),
LUA_INTERFACE_DEF(task, get_settings),
diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c
index a035eeaae..91b77c702 100644
--- a/src/plugins/fuzzy_check.c
+++ b/src/plugins/fuzzy_check.c
@@ -543,15 +543,13 @@ fuzzy_parse_rule(struct rspamd_config *cfg, const ucl_object_t *obj,
k = ucl_object_tostring(value);
if (k == NULL || (rule->peer_key =
- rspamd_pubkey_from_base32(k, 0, RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519)) == NULL) {
+ rspamd_pubkey_from_base32(k, 0, RSPAMD_KEYPAIR_KEX)) == NULL) {
msg_err_config("bad encryption key value: %s",
k);
return -1;
}
- rule->local_key = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX,
- RSPAMD_CRYPTOBOX_MODE_25519);
+ rule->local_key = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
}
if ((value = ucl_object_lookup(obj, "learn_condition")) != NULL) {
@@ -1334,8 +1332,7 @@ fuzzy_encrypt_cmd(struct fuzzy_rule *rule,
rule->local_key, rule->peer_key);
rspamd_cryptobox_encrypt_nm_inplace(data, datalen,
hdr->nonce, rspamd_pubkey_get_nm(rule->peer_key, rule->local_key),
- hdr->mac,
- rspamd_pubkey_alg(rule->peer_key));
+ hdr->mac);
}
static struct fuzzy_cmd_io *
@@ -2209,8 +2206,7 @@ fuzzy_process_reply(unsigned char **pos, int *r, GPtrArray *req,
sizeof(encrep.rep),
encrep.hdr.nonce,
rspamd_pubkey_get_nm(rule->peer_key, rule->local_key),
- encrep.hdr.mac,
- rspamd_pubkey_alg(rule->peer_key))) {
+ encrep.hdr.mac)) {
msg_info("cannot decrypt reply");
return NULL;
}
@@ -2299,6 +2295,8 @@ fuzzy_insert_result(struct fuzzy_client_session *session,
* Otherwise `value` means error code
*/
+ msg_debug_fuzzy_check("got reply with probability %.2f and value %.2f",
+ (double) rep->v1.prob, (double) rep->v1.value);
nval = fuzzy_normalize(rep->v1.value, weight);
if (io) {
diff --git a/src/plugins/lua/arc.lua b/src/plugins/lua/arc.lua
index ff19aef4c..90e254e78 100644
--- a/src/plugins/lua/arc.lua
+++ b/src/plugins/lua/arc.lua
@@ -635,11 +635,21 @@ local function prepare_arc_selector(task, sel)
end
end
+ local function arc_result_from_ar(ar_header)
+ ar_header = ar_header or ""
+ for k, v in string.gmatch(ar_header, "(%w+)=(%w+)") do
+ if k == 'arc' then
+ return v
+ end
+ end
+ return nil
+ end
+
if settings.reuse_auth_results then
local ar_header = task:get_header('Authentication-Results')
if ar_header then
- local arc_match = string.match(ar_header, 'arc=(%w+)')
+ local arc_match = arc_result_from_ar(ar_header)
if arc_match then
if arc_match == 'none' or arc_match == 'pass' then
diff --git a/src/plugins/lua/gpt.lua b/src/plugins/lua/gpt.lua
index 6adbce3bf..823dbd045 100644
--- a/src/plugins/lua/gpt.lua
+++ b/src/plugins/lua/gpt.lua
@@ -27,13 +27,11 @@ gpt {
# Your key to access the API
api_key = "xxx";
# Model name
- model = "gpt-3.5-turbo";
+ model = "gpt-4o-mini";
# Maximum tokens to generate
max_tokens = 1000;
# Temperature for sampling
- temperature = 0.7;
- # Top p for sampling
- top_p = 0.9;
+ temperature = 0.0;
# Timeout for requests
timeout = 10s;
# Prompt for the model (use default if not set)
@@ -71,10 +69,9 @@ local default_symbols_to_except = {
local settings = {
type = 'openai',
api_key = nil,
- model = 'gpt-3.5-turbo',
+ model = 'gpt-4o-mini',
max_tokens = 1000,
- temperature = 0.7,
- top_p = 0.9,
+ temperature = 0.0,
timeout = 10,
prompt = nil,
condition = nil,
@@ -97,11 +94,11 @@ local function default_condition(task)
local action = result.action
if action == 'reject' and result.npositive > 1 then
- return true, 'already decided as spam'
+ return false, 'already decided as spam'
end
if action == 'no action' and score < 0 then
- return true, 'negative score, already decided as ham'
+ return false, 'negative score, already decided as ham'
end
end
-- We also exclude some symbols
@@ -109,10 +106,12 @@ local function default_condition(task)
if task:has_symbol(s) then
if required_weight > 0 then
-- Also check score
- local sym = task:get_symbol(s)
+ local sym = task:get_symbol(s) or E
-- Must exist as we checked it before with `has_symbol`
- if math.abs(sym.weight) >= required_weight then
- return false, 'skip as "' .. s .. '" is found (weight: ' .. sym.weight .. ')'
+ if sym.weight then
+ if math.abs(sym.weight) >= required_weight then
+ return false, 'skip as "' .. s .. '" is found (weight: ' .. sym.weight .. ')'
+ end
end
lua_util.debugm(N, task, 'symbol %s has weight %s, but required %s', s,
sym.weight, required_weight)
@@ -195,6 +194,18 @@ local function default_conversion(task, input)
if type(reply) == 'table' and reply.probability then
local spam_score = tonumber(reply.probability)
+
+ if not spam_score then
+ -- Maybe we need GPT to convert GPT reply here?
+ if reply.probability == "high" then
+ spam_score = 0.9
+ elseif reply.probability == "low" then
+ spam_score = 0.1
+ else
+ rspamd_logger.infox("cannot convert to spam probability: %s", reply.probability)
+ end
+ end
+
if type(reply.usage) == 'table' then
rspamd_logger.infox(task, 'usage: %s tokens', reply.usage.total_tokens)
end
@@ -276,7 +287,7 @@ local function openai_gpt_check(task)
model = settings.model,
max_tokens = settings.max_tokens,
temperature = settings.temperature,
- top_p = settings.top_p,
+ response_format = { type = "json_object" },
messages = {
{
role = 'system',
@@ -348,8 +359,8 @@ if opts then
end
if not settings.prompt then
- settings.prompt = "You will be provided with the email message, " ..
- "and your task is to classify its probability to be spam, " ..
+ settings.prompt = "You will be provided with the email message, subject, from and url domains, " ..
+ "and your task is to evaluate the probability to be spam as number from 0 to 1, " ..
"output result as JSON with 'probability' field."
end
diff --git a/src/plugins/lua/history_redis.lua b/src/plugins/lua/history_redis.lua
index 3365b30cd..fff9f46b3 100644
--- a/src/plugins/lua/history_redis.lua
+++ b/src/plugins/lua/history_redis.lua
@@ -21,8 +21,8 @@ if confighelp then
redis_history {
# History key name
key_prefix = 'rs_history{{HOSTNAME}}{{COMPRESS}}';
- # History expire in seconds
- expire = 0;
+ # Expire in seconds for inactive keys, default to 5 days
+ expire = 432000;
# History rows limit
nrows = 200;
# Use zstd compression when storing data in redis
diff --git a/src/plugins/lua/known_senders.lua b/src/plugins/lua/known_senders.lua
index 6d57acea3..5cb2ddcf5 100644
--- a/src/plugins/lua/known_senders.lua
+++ b/src/plugins/lua/known_senders.lua
@@ -18,6 +18,7 @@ limitations under the License.
local rspamd_logger = require "rspamd_logger"
local N = 'known_senders'
+local E = {}
local lua_util = require "lua_util"
local lua_redis = require "lua_redis"
local lua_maps = require "lua_maps"
@@ -258,7 +259,7 @@ local function verify_local_replies_set(task)
return nil
end
- local replies_recipients = task:get_recipients('mime')
+ local replies_recipients = task:get_recipients('mime') or E
local replies_sender_string = lua_util.maybe_obfuscate_string(tostring(replies_sender), settings,
settings.sender_prefix)
diff --git a/src/plugins/lua/once_received.lua b/src/plugins/lua/once_received.lua
index 2a5552ab9..5c5ff7986 100644
--- a/src/plugins/lua/once_received.lua
+++ b/src/plugins/lua/once_received.lua
@@ -19,10 +19,7 @@ if confighelp then
end
-- 0 or 1 received: = spam
-
local symbol = 'ONCE_RECEIVED'
-local symbol_rdns = 'RDNS_NONE'
-local symbol_rdns_dnsfail = 'RDNS_DNSFAIL'
local symbol_mx = 'DIRECT_TO_MX'
-- Symbol for strict checks
local symbol_strict = nil
@@ -47,54 +44,6 @@ local function check_quantity_received (task)
return not h['flags']['artificial']
end, recvh))
- local function recv_dns_cb(_, to_resolve, results, err)
- if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then
- rspamd_logger.errx(task, 'error looking up %s: %s', to_resolve, err)
- task:insert_result(symbol_rdns_dnsfail, 1.0)
- end
-
- if not results then
- if nreceived <= 1 then
- task:insert_result(symbol, 1)
- -- Avoid strict symbol inserting as the remaining symbols have already
- -- quote a significant weight, so a message could be rejected by just
- -- this property.
- --task:insert_result(symbol_strict, 1)
- -- Check for MUAs
- local ua = task:get_header('User-Agent')
- local xm = task:get_header('X-Mailer')
- if (ua or xm) then
- task:insert_result(symbol_mx, 1, (ua or xm))
- end
- end
- task:insert_result(symbol_rdns, 1)
- else
- rspamd_logger.infox(task, 'source hostname has not been passed to Rspamd from MTA, ' ..
- 'but we could resolve source IP address PTR %s as "%s"',
- to_resolve, results[1])
- task:set_hostname(results[1])
-
- if good_hosts then
- for _, gh in ipairs(good_hosts) do
- if string.find(results[1], gh) then
- return
- end
- end
- end
-
- if nreceived <= 1 then
- task:insert_result(symbol, 1)
- for _, h in ipairs(bad_hosts) do
- if string.find(results[1], h) then
-
- task:insert_result(symbol_strict, 1, h)
- return
- end
- end
- end
- end
- end
-
local task_ip = task:get_ip()
if ((not check_authed and task:get_user()) or
@@ -110,13 +59,39 @@ local function check_quantity_received (task)
local hn = task:get_hostname()
-- Here we don't care about received
- if (not hn) and task_ip and task_ip:is_valid() then
- task:get_resolver():resolve_ptr({ task = task,
- name = task_ip:to_string(),
- callback = recv_dns_cb,
- forced = true
- })
+ if not hn then
+ if nreceived <= 1 then
+ task:insert_result(symbol, 1)
+ -- Avoid strict symbol inserting as the remaining symbols have already
+ -- quote a significant weight, so a message could be rejected by just
+ -- this property.
+ --task:insert_result(symbol_strict, 1)
+ -- Check for MUAs
+ local ua = task:get_header('User-Agent')
+ local xm = task:get_header('X-Mailer')
+ if (ua or xm) then
+ task:insert_result(symbol_mx, 1, (ua or xm))
+ end
+ end
return
+ else
+ if good_hosts then
+ for _, gh in ipairs(good_hosts) do
+ if string.find(hn, gh) then
+ return
+ end
+ end
+ end
+
+ if nreceived <= 1 then
+ task:insert_result(symbol, 1)
+ for _, h in ipairs(bad_hosts) do
+ if string.find(hn, h) then
+ task:insert_result(symbol_strict, 1, h)
+ break
+ end
+ end
+ end
end
if nreceived <= 1 then
@@ -181,10 +156,6 @@ if opts then
for n, v in pairs(opts) do
if n == 'symbol_strict' then
symbol_strict = v
- elseif n == 'symbol_rdns' then
- symbol_rdns = v
- elseif n == 'symbol_rdns_dnsfail' then
- symbol_rdns_dnsfail = v
elseif n == 'bad_host' then
if type(v) == 'string' then
bad_hosts[1] = v
@@ -207,16 +178,6 @@ if opts then
end
rspamd_config:register_symbol({
- name = symbol_rdns,
- type = 'virtual',
- parent = id
- })
- rspamd_config:register_symbol({
- name = symbol_rdns_dnsfail,
- type = 'virtual',
- parent = id
- })
- rspamd_config:register_symbol({
name = symbol_strict,
type = 'virtual',
parent = id
diff --git a/src/plugins/lua/spf.lua b/src/plugins/lua/spf.lua
index 48f3c17be..356507250 100644
--- a/src/plugins/lua/spf.lua
+++ b/src/plugins/lua/spf.lua
@@ -56,6 +56,7 @@ local symbols = {
dnsfail = "R_SPF_DNSFAIL",
permfail = "R_SPF_PERMFAIL",
na = "R_SPF_NA",
+ plusall = "R_SPF_PLUSALL",
}
local default_config = {
@@ -118,6 +119,8 @@ local function spf_check_callback(task)
local function flag_to_symbol(fl)
if bit.band(fl, rspamd_spf.flags.temp_fail) ~= 0 then
return local_config.symbols.dnsfail
+ elseif bit.band(fl, rspamd_spf.flags.plusall) ~= 0 then
+ return local_config.symbols.plusall
elseif bit.band(fl, rspamd_spf.flags.perm_fail) ~= 0 then
return local_config.symbols.permfail
elseif bit.band(fl, rspamd_spf.flags.na) ~= 0 then
diff --git a/src/rspamadm/configdump.c b/src/rspamadm/configdump.c
index 167b4c891..456875cf2 100644
--- a/src/rspamadm/configdump.c
+++ b/src/rspamadm/configdump.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Vsevolod Stakhov
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -445,6 +445,9 @@ rspamadm_configdump(int argc, char **argv, const struct rspamadm_command *cmd)
ucl_object_fromdouble(gr->max_score),
"max_score", strlen("max_score"), false);
ucl_object_insert_key(gr_ucl,
+ ucl_object_fromdouble(gr->min_score),
+ "min_score", strlen("min_score"), false);
+ ucl_object_insert_key(gr_ucl,
ucl_object_fromstring(gr->description),
"description", strlen("description"), false);
diff --git a/src/rspamadm/signtool.c b/src/rspamadm/signtool.c
index 612a67c83..ddc3d45df 100644
--- a/src/rspamadm/signtool.c
+++ b/src/rspamadm/signtool.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,7 +27,6 @@
#include <sys/wait.h>
#endif
-static gboolean openssl = FALSE;
static gboolean verify = FALSE;
static gboolean quiet = FALSE;
static char *suffix = NULL;
@@ -37,7 +36,6 @@ static char *pubout = NULL;
static char *keypair_file = NULL;
static char *editor = NULL;
static gboolean edit = FALSE;
-enum rspamd_cryptobox_mode mode = RSPAMD_CRYPTOBOX_MODE_25519;
static void rspamadm_signtool(int argc, char **argv,
const struct rspamadm_command *cmd);
@@ -53,8 +51,6 @@ struct rspamadm_command signtool_command = {
};
static GOptionEntry entries[] = {
- {"openssl", 'o', 0, G_OPTION_ARG_NONE, &openssl,
- "Generate openssl nistp256 keypair not curve25519 one", NULL},
{"verify", 'v', 0, G_OPTION_ARG_NONE, &verify,
"Verify signatures and not sign", NULL},
{"suffix", 'S', 0, G_OPTION_ARG_STRING, &suffix,
@@ -327,11 +323,8 @@ rspamadm_sign_file(const char *fname, struct rspamd_cryptobox_keypair *kp)
exit(EXIT_FAILURE);
}
- g_assert(rspamd_cryptobox_MAX_SIGBYTES >=
- rspamd_cryptobox_signature_bytes(mode));
-
sk = rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL);
- rspamd_cryptobox_sign(sig, NULL, map, st.st_size, sk, mode);
+ rspamd_cryptobox_sign(sig, NULL, map, st.st_size, sk);
if (edit) {
/* We also need to rename .new file */
@@ -348,7 +341,7 @@ rspamadm_sign_file(const char *fname, struct rspamd_cryptobox_keypair *kp)
rspamd_snprintf(sigpath, sizeof(sigpath), "%s%s", fname, suffix);
- if (write(fd_sig, sig, rspamd_cryptobox_signature_bytes(mode)) == -1) {
+ if (write(fd_sig, sig, crypto_sign_bytes()) == -1) {
rspamd_fprintf(stderr, "cannot write signature to %s: %s\n", sigpath,
strerror(errno));
exit(EXIT_FAILURE);
@@ -400,9 +393,6 @@ rspamadm_verify_file(const char *fname, const unsigned char *pk)
struct stat st, st_sig;
bool ret;
- g_assert(rspamd_cryptobox_MAX_SIGBYTES >=
- rspamd_cryptobox_signature_bytes(mode));
-
if (suffix == NULL) {
suffix = ".sig";
}
@@ -439,7 +429,7 @@ rspamadm_verify_file(const char *fname, const unsigned char *pk)
g_assert(fstat(fd_sig, &st_sig) != -1);
- if (st_sig.st_size != rspamd_cryptobox_signature_bytes(mode)) {
+ if (st_sig.st_size != crypto_sign_bytes()) {
close(fd_sig);
rspamd_fprintf(stderr, "invalid signature size %s: %ud\n", fname,
(unsigned int) st_sig.st_size);
@@ -458,7 +448,7 @@ rspamadm_verify_file(const char *fname, const unsigned char *pk)
}
ret = rspamd_cryptobox_verify(map_sig, st_sig.st_size,
- map, st.st_size, pk, mode);
+ map, st.st_size, pk);
munmap(map, st.st_size);
munmap(map_sig, st_sig.st_size);
@@ -503,10 +493,6 @@ rspamadm_signtool(int argc, char **argv, const struct rspamadm_command *cmd)
g_option_context_free(context);
- if (openssl) {
- mode = RSPAMD_CRYPTOBOX_MODE_NIST;
- }
-
if (verify && (!pubkey && !pubkey_file)) {
rspamd_fprintf(stderr, "no pubkey for verification\n");
exit(EXIT_FAILURE);
@@ -549,14 +535,13 @@ rspamadm_signtool(int argc, char **argv, const struct rspamadm_command *cmd)
flen--;
}
- pk = rspamd_pubkey_from_base32(map, flen,
- RSPAMD_KEYPAIR_SIGN, mode);
+ pk = rspamd_pubkey_from_base32(map, flen, RSPAMD_KEYPAIR_SIGN);
if (pk == NULL) {
rspamd_fprintf(stderr, "bad size %s: %ud, %ud expected\n",
pubkey_file,
(unsigned int) flen,
- rspamd_cryptobox_pk_sig_bytes(mode));
+ crypto_sign_publickeybytes());
exit(EXIT_FAILURE);
}
@@ -564,13 +549,13 @@ rspamadm_signtool(int argc, char **argv, const struct rspamadm_command *cmd)
}
else {
pk = rspamd_pubkey_from_base32(pubkey, strlen(pubkey),
- RSPAMD_KEYPAIR_SIGN, mode);
+ RSPAMD_KEYPAIR_SIGN);
if (pk == NULL) {
rspamd_fprintf(stderr, "bad size %s: %ud, %ud expected\n",
pubkey_file,
(unsigned int) strlen(pubkey),
- rspamd_cryptobox_pk_sig_bytes(mode));
+ crypto_sign_publickeybytes());
exit(EXIT_FAILURE);
}
}
diff --git a/src/rspamd_proxy.c b/src/rspamd_proxy.c
index 4f08e81b9..dbdd2e5a7 100644
--- a/src/rspamd_proxy.c
+++ b/src/rspamd_proxy.c
@@ -395,7 +395,7 @@ rspamd_proxy_parse_upstream(rspamd_mempool_t *pool,
elt = ucl_object_lookup(obj, "key");
if (elt != NULL) {
up->key = rspamd_pubkey_from_base32(ucl_object_tostring(elt), 0,
- RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
+ RSPAMD_KEYPAIR_KEX);
if (up->key == NULL) {
g_set_error(err, rspamd_proxy_quark(), 100,
@@ -571,7 +571,7 @@ rspamd_proxy_parse_mirror(rspamd_mempool_t *pool,
elt = ucl_object_lookup(obj, "key");
if (elt != NULL) {
up->key = rspamd_pubkey_from_base32(ucl_object_tostring(elt), 0,
- RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
+ RSPAMD_KEYPAIR_KEX);
if (up->key == NULL) {
g_set_error(err, rspamd_proxy_quark(), 100,
@@ -1398,7 +1398,8 @@ proxy_backend_mirror_finish_handler(struct rspamd_http_connection *conn,
bk_conn->err = "cannot parse ucl";
}
- msg_info_session("finished mirror connection to %s", bk_conn->name);
+ msg_info_session("finished mirror connection to %s; HTTP code: %d",
+ bk_conn->name, msg->code);
rspamd_upstream_ok(bk_conn->up);
proxy_backend_close_connection(bk_conn);
@@ -2203,6 +2204,8 @@ proxy_client_finish_handler(struct rspamd_http_connection *conn,
rspamd_http_message_remove_header(msg, "Keep-Alive");
rspamd_http_message_remove_header(msg, "Connection");
rspamd_http_message_remove_header(msg, "Key");
+ rspamd_http_message_add_header_len(msg, LOG_TAG_HEADER, session->pool->tag.uid,
+ sizeof(session->pool->tag.uid));
proxy_open_mirror_connections(session);
rspamd_http_connection_reset(session->client_conn);
@@ -2210,7 +2213,10 @@ proxy_client_finish_handler(struct rspamd_http_connection *conn,
proxy_send_master_message(session);
}
else {
- msg_info_session("finished master connection");
+ msg_info_session("finished master connection to %s; HTTP code: %d",
+ rspamd_inet_address_to_string_pretty(
+ rspamd_upstream_addr_cur(session->master_conn->up)),
+ msg->code);
proxy_backend_close_connection(session->master_conn);
REF_RELEASE(session);
}
diff --git a/test/functional/cases/001_merged/101_lua.robot b/test/functional/cases/001_merged/101_lua.robot
index 2cfc03677..51d5b4b23 100644
--- a/test/functional/cases/001_merged/101_lua.robot
+++ b/test/functional/cases/001_merged/101_lua.robot
@@ -52,4 +52,48 @@ External Maps Simple
Task Inject Url
Scan File ${URL_ICS} Settings={symbols_enabled = [TEST_INJECT_URL]}
- Expect Symbol TEST_INJECT_URL \ No newline at end of file
+ Expect Symbol TEST_INJECT_URL
+
+Group Score Positive
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_POSITIVE1, GR_POSITIVE2, GR_POSITIVE4, GR_POSITIVE8, GR_POSITIVE16]}
+ Expect Symbol With Score GR_POSITIVE1 1
+ Expect Symbol With Score GR_POSITIVE2 2
+ Expect Symbol With Score GR_POSITIVE4 4
+ Expect Symbol With Score GR_POSITIVE8 3
+ Expect Symbol With Score GR_POSITIVE16 0
+
+Group Score Negative
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_NEGATIVE1, GR_NEGATIVE2, GR_NEGATIVE4, GR_NEGATIVE8]}
+ Expect Symbol With Score GR_NEGATIVE1 -1
+ Expect Symbol With Score GR_NEGATIVE2 -2
+ Expect Symbol With Score GR_NEGATIVE4 -4
+ Expect Symbol With Score GR_NEGATIVE8 -3
+
+Group Score Mix 1
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_POSITIVE1, GR_POSITIVE2, GR_POSITIVE4, GR_POSITIVE8, GR_NEGATIVE1, GR_NEGATIVE2, GR_NEGATIVE4, GR_NEGATIVE8]}
+ Expect Symbol With Score GR_POSITIVE1 1
+ Expect Symbol With Score GR_POSITIVE2 2
+ Expect Symbol With Score GR_POSITIVE4 4
+ Expect Symbol With Score GR_POSITIVE8 3
+ Expect Symbol With Score GR_NEGATIVE1 -1
+ Expect Symbol With Score GR_NEGATIVE2 -2
+ Expect Symbol With Score GR_NEGATIVE4 -4
+ Expect Symbol With Score GR_NEGATIVE8 -8
+
+Group Score Mix 2
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_POSITIVE1, GR_POSITIVE2, GR_POSITIVE4, GR_POSITIVE8, GR_NEGATIVE16]}
+ Expect Symbol With Score GR_POSITIVE1 1
+ Expect Symbol With Score GR_POSITIVE2 2
+ Expect Symbol With Score GR_POSITIVE4 4
+ Expect Symbol With Score GR_POSITIVE8 3
+ Expect Symbol With Score GR_NEGATIVE16 -16
+
+Group Score Mix 3
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_POSITIVE1, GR_NEGATIVE16]}
+ Expect Symbol With Score GR_POSITIVE1 1
+ Expect Symbol With Score GR_NEGATIVE16 -11
+
+Group Score Mix 4
+ Scan File ${MESSAGE} Settings={symbols_enabled = [GR_POSITIVE16, GR_NEGATIVE16]}
+ Expect Symbol With Score GR_POSITIVE16 10
+ Expect Symbol With Score GR_NEGATIVE16 -16 \ No newline at end of file
diff --git a/test/functional/cases/001_merged/117_spf.robot b/test/functional/cases/001_merged/117_spf.robot
index dda35f671..a8b35a8c9 100644
--- a/test/functional/cases/001_merged/117_spf.robot
+++ b/test/functional/cases/001_merged/117_spf.robot
@@ -155,3 +155,9 @@ SPF UPPERCASE
... IP=8.8.8.8 From=x@fail11.org.org.za
... Settings=${SETTINGS_SPF}
Expect Symbol R_SPF_ALLOW
+
+SPF PLUSALL
+ Scan File ${RSPAMD_TESTDIR}/messages/dmarc/bad_dkim1.eml
+ ... IP=8.8.8.8 From=x@plusall.com
+ ... Settings=${SETTINGS_SPF}
+ Expect Symbol R_SPF_PLUSALL
diff --git a/test/functional/cases/450_grow_factor.robot b/test/functional/cases/450_grow_factor.robot
index 518c3ed82..85ca08c68 100644
--- a/test/functional/cases/450_grow_factor.robot
+++ b/test/functional/cases/450_grow_factor.robot
@@ -1,6 +1,7 @@
*** Settings ***
Suite Setup Rspamd Setup
Suite Teardown Rspamd Teardown
+Library ${RSPAMD_TESTDIR}/lib/grow_factor.py
Library ${RSPAMD_TESTDIR}/lib/rspamd.py
Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot
Variables ${RSPAMD_TESTDIR}/lib/vars.py
@@ -9,29 +10,17 @@ Variables ${RSPAMD_TESTDIR}/lib/vars.py
${CONFIG} ${RSPAMD_TESTDIR}/configs/grow_factor.conf
${HAM_MESSAGE} ${RSPAMD_TESTDIR}/messages/ham.eml
${RSPAMD_SCOPE} Suite
-&{RESCORED_SYMBOLS}
-... SIMPLE_TEST_001=0.013067
-... SIMPLE_TEST_002=14.374194
-... SIMPLE_TEST_003=6.533724
-... SIMPLE_TEST_004=13.067449
-... SIMPLE_TEST_005=0.013067
-... SIMPLE_TEST_006=0.130674
-... SIMPLE_TEST_007=0.143741
-... SIMPLE_TEST_008=0.156809
-... SIMPLE_TEST_009=0.169876
-... SIMPLE_TEST_010=0.182944
-... SIMPLE_TEST_011=-0.010000
-... SIMPLE_TEST_012=-0.100000
-... SIMPLE_TEST_013=-10.000000
*** Test Cases ***
CHECK BASIC
Scan File ${HAM_MESSAGE}
... Settings={groups_enabled = [simple_tests]}
Expect Required Score 15
+ &{RESCORED_SYMBOLS} = Apply Grow Factor 1.1 15
Expect Symbols With Scores &{RESCORED_SYMBOLS}
CHECK NOREJECT
Scan File ${HAM_MESSAGE}
... Settings={actions { reject = null, "add header" = 15 }, groups_enabled = [simple_tests]}
+ &{RESCORED_SYMBOLS} = Apply Grow Factor 1.1 15
Expect Symbols With Scores &{RESCORED_SYMBOLS}
diff --git a/test/functional/cases/550_milter_headers.robot b/test/functional/cases/550_milter_headers.robot
new file mode 100644
index 000000000..80471b83c
--- /dev/null
+++ b/test/functional/cases/550_milter_headers.robot
@@ -0,0 +1,39 @@
+*** Settings ***
+Suite Setup Rspamd Setup
+Suite Teardown Rspamd Teardown
+Library ${RSPAMD_TESTDIR}/lib/rspamd.py
+Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot
+Variables ${RSPAMD_TESTDIR}/lib/vars.py
+
+*** Variables ***
+${CONFIG} ${RSPAMD_TESTDIR}/configs/milter_headers.conf
+${MESSAGE} ${RSPAMD_TESTDIR}/messages/zip.eml
+${RSPAMD_SCOPE} Suite
+${RSPAMD_URL_TLD} ${RSPAMD_TESTDIR}/../lua/unit/test_tld.dat
+${SETTINGS_NOSYMBOLS} {symbols_enabled = []}
+${SETTINGS_TEST} {SIMPLE_TEST = 2.0, symbols_enabled = [SIMPLE_TEST]}
+
+*** Test Cases ***
+CHECK HEADERS WITH TEST SYMBOL
+ Scan File ${MESSAGE} Settings=${SETTINGS_TEST}
+ # Check X-Virus header
+ Expect Removed Header X-Virus
+ Expect Added Header X-Virus Fires always
+ # Check My-Spamd-Bar header
+ Expect Added Header My-Spamd-Bar ++
+ Do Not Expect Removed Header My-Spamd-Bar
+ # Check X-Spam-Level header
+ Expect Added Header X-Spam-Level **
+ Expect Removed Header X-Spam-Level
+
+CHECK HEADERS WITHOUT TEST SYMBOL
+ Scan File ${MESSAGE} Settings=${SETTINGS_NOSYMBOLS}
+ # Check X-Virus header
+ Expect Removed Header X-Virus
+ Do Not Expect Added Header X-Virus
+ # Check My-Spamd-Bar header
+ Expect Added Header My-Spamd-Bar /
+ Do Not Expect Removed Header My-Spamd-Bar
+ # Check X-Spam-Level header
+ Do Not Expect Added Header X-Spam-Level
+ Expect Removed Header X-Spam-Level
diff --git a/test/functional/configs/grow_factor-local.conf b/test/functional/configs/grow_factor-local.conf
index cdf1bf436..80b60b43b 100644
--- a/test/functional/configs/grow_factor-local.conf
+++ b/test/functional/configs/grow_factor-local.conf
@@ -6,3 +6,7 @@ metric {
greylist = 4;
}
}
+
+options {
+ check_all_filters = true;
+} \ No newline at end of file
diff --git a/test/functional/configs/merged-local.conf b/test/functional/configs/merged-local.conf
index 75b9f0554..2aef274c2 100644
--- a/test/functional/configs/merged-local.conf
+++ b/test/functional/configs/merged-local.conf
@@ -229,6 +229,11 @@ options = {
replies = ["v=spf1 -all"];
},
{
+ name = "plusall.com",
+ type = "txt";
+ replies = ["v=spf1 +all"];
+ },
+ {
name = "fail4.org.org.za",
type = "txt";
replies = ["v=spf1 redirect=asdfsfewewrredfs"];
@@ -960,6 +965,45 @@ symbols {
}
}
+group "test" {
+ max_score = 10;
+ min_score = -10;
+
+ symbols = {
+ "GR_POSITIVE1" = {
+ score = 1.0;
+ },
+ "GR_POSITIVE2" = {
+ score = 2.0;
+ },
+ "GR_POSITIVE4" = {
+ score = 4.0;
+ },
+ "GR_POSITIVE8" = {
+ score = 8.0;
+ },
+ "GR_POSITIVE16" = {
+ score = 16.0;
+ },
+
+ "GR_NEGATIVE1" = {
+ score = -1.0;
+ },
+ "GR_NEGATIVE2" = {
+ score = -2.0;
+ },
+ "GR_NEGATIVE4" = {
+ score = -4.0;
+ },
+ "GR_NEGATIVE8" = {
+ score = -8.0;
+ },
+ "GR_NEGATIVE16" = {
+ score = -16.0;
+ },
+ }
+}
+
worker "controller" {
bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_CONTROLLER =}";
keypair {
diff --git a/test/functional/configs/merged.conf b/test/functional/configs/merged.conf
index 0ee224ceb..06a34308d 100644
--- a/test/functional/configs/merged.conf
+++ b/test/functional/configs/merged.conf
@@ -11,6 +11,7 @@ lua = "{= env.TESTDIR =}/lua/recipients.lua"
lua = "{= env.TESTDIR =}/lua/remove_result.lua"
lua = "{= env.TESTDIR =}/lua/tlds.lua"
lua = "{= env.TESTDIR =}/lua/inject_url.lua"
+lua = "{= env.TESTDIR =}/lua/limits.lua"
# 104_get_from
lua = "{= env.TESTDIR =}/lua/get_from.lua"
diff --git a/test/functional/configs/milter_headers.conf b/test/functional/configs/milter_headers.conf
new file mode 100644
index 000000000..947bc28dd
--- /dev/null
+++ b/test/functional/configs/milter_headers.conf
@@ -0,0 +1,24 @@
+lua = "{= env.TESTDIR =}/lua/simple.lua"
+
+.include(duplicate=append,priority=0) "{= env.TESTDIR =}/configs/plugins.conf"
+
+milter_headers {
+
+ use = ["remove-headers", "x-spam-level", "x-spamd-bar", "x-virus"];
+
+ routines {
+ remove-headers {
+ headers {
+ "X-Spam-Level" = 0,
+ }
+ }
+ x-spamd-bar {
+ header = "My-Spamd-Bar";
+ remove = null;
+ }
+ x-virus {
+ symbols = ["SIMPLE_TEST"];
+ }
+ }
+
+}
diff --git a/test/functional/lib/grow_factor.py b/test/functional/lib/grow_factor.py
new file mode 100644
index 000000000..486a186e8
--- /dev/null
+++ b/test/functional/lib/grow_factor.py
@@ -0,0 +1,32 @@
+from robot.libraries.BuiltIn import BuiltIn
+
+def Apply_Grow_Factor(grow_factor, max_limit):
+ grow_factor = float(grow_factor)
+ max_limit = float(max_limit)
+ expected_result = {}
+ res = BuiltIn().get_variable_value("${SCAN_RESULT}")
+
+ for sym, p in res["symbols"].items():
+ expected_result[sym] = p["score"]
+
+ if grow_factor <= 1.0:
+ return expected_result
+
+ if max_limit <= 0:
+ return expected_result
+
+ final_grow_factor = grow_factor
+ mult = grow_factor - 1.0
+ for sym, p in res["symbols"].items():
+ if p["score"] <= 0:
+ continue
+ mult *= round(p["score"] / max_limit, 2)
+ final_grow_factor *= round(1.0 + mult, 2)
+
+ if final_grow_factor <= 1.0:
+ return expected_result
+
+ for sym, p in res["symbols"].items():
+ if p["score"] <= 0:
+ continue
+ expected_result[sym] = round(p["score"] * final_grow_factor, 2)
diff --git a/test/functional/lib/rspamd.robot b/test/functional/lib/rspamd.robot
index 254a6f3ca..de4e5285f 100644
--- a/test/functional/lib/rspamd.robot
+++ b/test/functional/lib/rspamd.robot
@@ -60,6 +60,28 @@ Check Rspamc Match String
Should Not Contain ${subject} ${str}
END
+Do Not Expect Added Header
+ [Arguments] ${header_name}
+ IF 'milter' not in ${SCAN_RESULT}
+ RETURN
+ END
+ IF 'add_headers' not in ${SCAN_RESULT}[milter]
+ RETURN
+ END
+ Dictionary Should Not Contain Key ${SCAN_RESULT}[milter][add_headers] ${header_name}
+ ... msg=${header_name} was added
+
+Do Not Expect Removed Header
+ [Arguments] ${header_name}
+ IF 'milter' not in ${SCAN_RESULT}
+ RETURN
+ END
+ IF 'remove_headers' not in ${SCAN_RESULT}[milter]
+ RETURN
+ END
+ Dictionary Should Not Contain Key ${SCAN_RESULT}[milter][remove_headers] ${header_name}
+ ... msg=${header_name} was removed
+
Do Not Expect Symbol
[Arguments] ${symbol}
Dictionary Should Not Contain Key ${SCAN_RESULT}[symbols] ${symbol}
@@ -76,10 +98,31 @@ Expect Action
[Arguments] ${action}
Should Be Equal ${SCAN_RESULT}[action] ${action}
+Expect Added Header
+ [Arguments] ${header_name} ${header_value} ${pos}=-1
+ Dictionary Should Contain Key ${SCAN_RESULT} milter
+ ... msg=milter block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter] add_headers
+ ... msg=add_headers block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter][add_headers] ${header_name}
+ ... msg=${header_name} was not added
+ Should Be Equal ${SCAN_RESULT}[milter][add_headers][${header_name}][value] ${header_value}
+ Should Be Equal as Numbers ${SCAN_RESULT}[milter][add_headers][${header_name}][order] ${pos}
+
Expect Email
[Arguments] ${email}
List Should Contain Value ${SCAN_RESULT}[emails] ${email}
+Expect Removed Header
+ [Arguments] ${header_name} ${pos}=0
+ Dictionary Should Contain Key ${SCAN_RESULT} milter
+ ... msg=milter block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter] remove_headers
+ ... msg=remove_headers block was not present in protocol response
+ Dictionary Should Contain Key ${SCAN_RESULT}[milter][remove_headers] ${header_name}
+ ... msg=${header_name} was not removed
+ Should Be Equal as Numbers ${SCAN_RESULT}[milter][remove_headers][${header_name}] ${pos}
+
Expect Required Score
[Arguments] ${required_score}
Should Be Equal As Numbers ${SCAN_RESULT}[required_score] ${required_score}
@@ -302,7 +345,22 @@ Run Rspamd
Export Scoped Variables ${RSPAMD_SCOPE} RSPAMD_PROCESS=${result}
# Confirm worker is reachable
- Wait Until Keyword Succeeds 15x 1 sec Ping Rspamd ${RSPAMD_LOCAL_ADDR} ${check_port}
+ FOR ${index} IN RANGE 37
+ ${ok} = Rspamd Startup Check ${check_port}
+ IF ${ok} CONTINUE
+ Sleep 0.4s
+ END
+
+Rspamd Startup Check
+ [Arguments] ${check_port}=${RSPAMD_PORT_NORMAL}
+ ${handle} = Get Process Object
+ ${res} = Evaluate $handle.poll()
+ IF ${res} != None
+ ${stderr} = Get File ${RSPAMD_TMPDIR}/rspamd.stderr
+ Fail Process Is Gone, stderr: ${stderr}
+ END
+ ${ping} = Run Keyword And Return Status Ping Rspamd ${RSPAMD_LOCAL_ADDR} ${check_port}
+ [Return] ${ping}
Rspamadm Setup
${RSPAMADM_TMPDIR} = Make Temporary Directory
diff --git a/test/functional/lua/deps.lua b/test/functional/lua/deps.lua
index 6171db699..b78d3abb7 100644
--- a/test/functional/lua/deps.lua
+++ b/test/functional/lua/deps.lua
@@ -24,7 +24,7 @@ rspamd_config:register_virtual_symbol('TOP', 1.0, id)
rspamd_config:register_symbol('DEP1', 1.0, cb_dep1)
rspamd_config:register_dependency('DEP1', 'TOP')
-for i = 2,10 do
+for i = 2, 10 do
rspamd_config:register_symbol('DEP' .. tostring(i), 1.0, cb_gen(i - 1))
rspamd_config:register_dependency('DEP' .. tostring(i), 'DEP' .. tostring(i - 1))
end
diff --git a/test/functional/lua/limits.lua b/test/functional/lua/limits.lua
new file mode 100644
index 000000000..52fb47fb9
--- /dev/null
+++ b/test/functional/lua/limits.lua
@@ -0,0 +1,22 @@
+local true_cb_gen = function()
+ return function()
+ return true
+ end
+end
+
+local test_weights = { 1, 2, 4, 8, 16 }
+for _, i in ipairs(test_weights) do
+ rspamd_config:register_symbol('GR_POSITIVE' .. tostring(i), 1.0, true_cb_gen())
+
+ if i > 1 then
+ rspamd_config:register_dependency('GR_POSITIVE' .. tostring(i), 'GR_POSITIVE' .. tostring(i / 2))
+ end
+
+ rspamd_config:register_symbol('GR_NEGATIVE' .. tostring(i), 1.0, true_cb_gen())
+
+ if i > 1 then
+ rspamd_config:register_dependency('GR_NEGATIVE' .. tostring(i), 'GR_NEGATIVE' .. tostring(i / 2))
+ end
+end
+
+rspamd_config:register_dependency('GR_NEGATIVE1', 'GR_POSITIVE16') \ No newline at end of file
diff --git a/test/functional/lua/simple_plus.lua b/test/functional/lua/simple_plus.lua
index 57aee6ae3..badde685f 100644
--- a/test/functional/lua/simple_plus.lua
+++ b/test/functional/lua/simple_plus.lua
@@ -1,7 +1,7 @@
local test_symbols = {
SIMPLE_TEST_001 = 0.01,
- SIMPLE_TEST_002 = 11,
- SIMPLE_TEST_003 = 5.0,
+ SIMPLE_TEST_002 = 5.5,
+ SIMPLE_TEST_003 = 2.5,
SIMPLE_TEST_004 = 10.0,
SIMPLE_TEST_005 = 0.01,
SIMPLE_TEST_006 = 0.10,
diff --git a/test/lua/unit/rsa.lua b/test/lua/unit/rsa.lua
index c67a36abb..019212df4 100644
--- a/test/lua/unit/rsa.lua
+++ b/test/lua/unit/rsa.lua
@@ -47,4 +47,14 @@ context("RSA signature verification test", function()
sk, pk = rsa.keypair()
assert_false(rsa.verify_memory(pk, sig, "test"))
end)
+
+ test("RSA-2048 keypair + sign + verify", function()
+ local sk, pk = rsa.keypair(2048)
+ local sig = rsa.sign_memory(sk, "test")
+ assert_true(rsa.verify_memory(pk, sig, "test"))
+ assert_false(rsa.verify_memory(pk, sig, "test1"))
+ -- Overwrite
+ sk, pk = rsa.keypair(2048)
+ assert_false(rsa.verify_memory(pk, sig, "test"))
+ end)
end)
diff --git a/test/lua/unit/ucl.lua b/test/lua/unit/ucl.lua
new file mode 100644
index 000000000..1b975d390
--- /dev/null
+++ b/test/lua/unit/ucl.lua
@@ -0,0 +1,75 @@
+-- Test some UCL stuff
+
+context("UCL manipulation", function()
+ local ucl = require "ucl"
+
+ local parser = ucl.parser()
+ local res, err = parser:parse_string('{"key":"val"}')
+ assert(res)
+
+ local reply = parser:get_object_wrapped()
+ local expected = {
+ key = 'ohlol',
+ ololo = 'ohlol'
+ }
+
+ test("UCL transparent test: object", function()
+ assert_equal(tostring(reply), '{"key":"val"}')
+ assert_equal(reply:type(), 'object')
+ assert_equal(reply:at('key'):unwrap(), 'val')
+ reply.ololo = 'ohlol'
+ reply.ololo = 'ohlol'
+ reply.key = 'ohlol'
+ assert_equal(reply:at('key'):unwrap(), 'ohlol')
+
+ for k, v in reply:pairs() do
+ assert_equal(expected[k], v:unwrap())
+ end
+ end)
+
+ test("UCL transparent test: array", function()
+ parser = ucl.parser()
+ res, err = parser:parse_string('["e1","e2"]')
+ assert(res)
+ local ireply = parser:get_object_wrapped()
+
+ assert_equal(tostring(ireply), '["e1","e2"]')
+ assert_equal(ireply:type(), 'array')
+ ireply[1] = 1
+ ireply[1] = 1
+ ireply[1] = 1
+ ireply[1] = 1
+ ireply[1] = 1
+ ireply[ireply:len() + 1] = 100500
+ local iexpected = { 1, "e2", 100500 }
+ for k, v in ireply:ipairs() do
+ assert_equal(v:unwrap(), iexpected[k])
+ end
+ end)
+
+ test("UCL transparent test: concat", function()
+ reply.tbl = ireply
+ expected.tbl = iexpected
+ for k, v in reply:pairs() do
+ if type(expected[k]) == 'table' then
+ for kk, vv in v:ipairs() do
+ assert_equal(expected[k][kk], vv:unwrap())
+ end
+ else
+ assert_equal(expected[k], v:unwrap())
+ end
+ end
+ end)
+
+ test("UCL transparent test: implicit conversion array->object", function()
+ -- Assign empty table, so it'll be an array
+ reply.t = {}
+ assert_equal(reply.t:type(), 'array')
+ -- We can convert empty table to object
+ reply.t.test = 'test'
+ assert_equal(reply.t:type(), 'object')
+ assert_equal(reply.t.test:unwrap(), 'test')
+ end)
+
+ collectgarbage() -- To ensure we don't crash with asan
+end) \ No newline at end of file
diff --git a/test/rspamd_cryptobox_test.c b/test/rspamd_cryptobox_test.c
index b32b2822b..03b833404 100644
--- a/test/rspamd_cryptobox_test.c
+++ b/test/rspamd_cryptobox_test.c
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2016 Vsevolod Stakhov
+/*
+ * Copyright 2024 Vsevolod Stakhov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -24,7 +24,6 @@
static const int mapping_size = 64 * 8192 + 1;
static const int max_seg = 32;
static const int random_fuzz_cnt = 10000;
-enum rspamd_cryptobox_mode mode = RSPAMD_CRYPTOBOX_MODE_25519;
static void *
create_mapping(int mapping_len, unsigned char **beg, unsigned char **end)
@@ -52,7 +51,7 @@ check_result(const rspamd_nm_t key, const rspamd_nonce_t nonce,
uint64_t *t = (uint64_t *) begin;
g_assert(rspamd_cryptobox_decrypt_nm_inplace(begin, end - begin, nonce, key,
- mac, mode));
+ mac));
while (t < (uint64_t *) end) {
g_assert(*t == 0);
@@ -174,33 +173,17 @@ void rspamd_cryptobox_test_func(void)
/* Test baseline */
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encrypt_nm_inplace(begin, end - begin, nonce, key, mac,
- mode);
+ rspamd_cryptobox_encrypt_nm_inplace(begin, end - begin, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
msg_info("baseline encryption: %.0f", t2 - t1);
- mode = RSPAMD_CRYPTOBOX_MODE_NIST;
- t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encrypt_nm_inplace(begin,
- end - begin,
- nonce,
- key,
- mac,
- mode);
- t2 = rspamd_get_ticks(TRUE);
- check_result(key, nonce, mac, begin, end);
-
- msg_info("openssl baseline encryption: %.0f", t2 - t1);
- mode = RSPAMD_CRYPTOBOX_MODE_25519;
-
-start:
/* A single chunk as vector */
seg[0].data = begin;
seg[0].len = end - begin;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 1, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 1, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -213,7 +196,7 @@ start:
seg[1].data = begin + seg[0].len;
seg[1].len = (end - begin) - seg[0].len;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -225,7 +208,7 @@ start:
seg[1].data = begin + seg[0].len;
seg[1].len = (end - begin) - seg[0].len;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -237,7 +220,7 @@ start:
seg[1].data = begin + seg[0].len;
seg[1].len = (end - begin) - seg[0].len;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -250,7 +233,7 @@ start:
seg[1].data = begin + seg[0].len;
seg[1].len = (end - begin) - seg[0].len;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 2, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -265,7 +248,7 @@ start:
seg[2].data = begin + seg[0].len + seg[1].len;
seg[2].len = (end - begin) - seg[0].len - seg[1].len;
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, 3, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, 3, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -274,7 +257,7 @@ start:
cnt = create_random_split(seg, max_seg, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -283,7 +266,7 @@ start:
cnt = create_realistic_split(seg, max_seg, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -292,7 +275,7 @@ start:
cnt = create_constrained_split(seg, max_seg + 1, 32, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -303,7 +286,7 @@ start:
ms = ottery_rand_range(i % max_seg * 2) + 1;
cnt = create_random_split(seg, ms, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -316,7 +299,7 @@ start:
ms = ottery_rand_range(i % max_seg * 2) + 1;
cnt = create_realistic_split(seg, ms, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -329,7 +312,7 @@ start:
ms = ottery_rand_range(i % max_seg * 10) + 1;
cnt = create_constrained_split(seg, ms, i, begin, end);
t1 = rspamd_get_ticks(TRUE);
- rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac, mode);
+ rspamd_cryptobox_encryptv_nm_inplace(seg, cnt, nonce, key, mac);
t2 = rspamd_get_ticks(TRUE);
check_result(key, nonce, mac, begin, end);
@@ -338,10 +321,4 @@ start:
msg_info("constrained fuzz iterations: %d", i);
}
}
-
- if (!checked_openssl) {
- checked_openssl = TRUE;
- mode = RSPAMD_CRYPTOBOX_MODE_NIST;
- goto start;
- }
}
diff --git a/test/rspamd_cxx_unit.cxx b/test/rspamd_cxx_unit.cxx
index 3dde89d1c..b7cb0c6bf 100644
--- a/test/rspamd_cxx_unit.cxx
+++ b/test/rspamd_cxx_unit.cxx
@@ -24,6 +24,7 @@
#include "rspamd_cxx_unit_utils.hxx"
#include "rspamd_cxx_local_ptr.hxx"
#include "rspamd_cxx_unit_dkim.hxx"
+#include "rspamd_cxx_unit_cryptobox.hxx"
static gboolean verbose = false;
static const GOptionEntry entries[] =
@@ -83,4 +84,4 @@ int main(int argc, char **argv)
rspamd_mempool_delete(pool);
return res;
-} \ No newline at end of file
+}
diff --git a/test/rspamd_cxx_unit_cryptobox.hxx b/test/rspamd_cxx_unit_cryptobox.hxx
new file mode 100644
index 000000000..5829b1e43
--- /dev/null
+++ b/test/rspamd_cxx_unit_cryptobox.hxx
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2024 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Detached unit tests for the cryptobox */
+
+#ifndef RSPAMD_RSPAMD_CXX_UNIT_CRYPTOBOX_HXX
+#define RSPAMD_RSPAMD_CXX_UNIT_CRYPTOBOX_HXX
+#include "libcryptobox/cryptobox.h"
+#include <string>
+#include <string_view>
+
+TEST_SUITE("rspamd_cryptobox")
+{
+
+ TEST_CASE("rspamd_cryptobox_keypair")
+ {
+ rspamd_sk_t sk;
+ rspamd_pk_t pk;
+
+ rspamd_cryptobox_keypair(pk, sk);
+ }
+
+ TEST_CASE("rspamd_cryptobox_keypair_sig")
+ {
+ rspamd_sig_sk_t sk;
+ rspamd_sig_pk_t pk;
+
+ rspamd_cryptobox_keypair_sig(pk, sk);
+ }
+
+ TEST_CASE("rspamd_cryptobox_hash")
+ {
+ rspamd_cryptobox_hash_state_t p = {0};
+ std::string key{"key"};
+
+ rspamd_cryptobox_hash_init(&p, reinterpret_cast<const unsigned char *>(key.data()), key.size());
+ std::string data{"key"};
+ rspamd_cryptobox_hash_update(&p, reinterpret_cast<const unsigned char *>(data.data()), data.size());
+
+ unsigned char out1[rspamd_cryptobox_HASHBYTES];
+ rspamd_cryptobox_hash_final(&p, out1);
+
+ unsigned char out2[rspamd_cryptobox_HASHBYTES];
+ rspamd_cryptobox_hash(out2,
+ reinterpret_cast<const unsigned char *>(data.data()), data.size(),
+ reinterpret_cast<const unsigned char *>(key.data()), key.size());
+ CHECK(memcmp(out1, out2, sizeof(out1)) == 0);
+ }
+
+ TEST_CASE("rspamd_cryptobox_fast_hash")
+ {
+ rspamd_cryptobox_fast_hash_state_s *st = rspamd_cryptobox_fast_hash_new();
+ uint64_t seed = 10;
+ rspamd_cryptobox_fast_hash_init(st, seed);
+ std::string data{"key"};
+
+ rspamd_cryptobox_fast_hash_update(st,
+ reinterpret_cast<const unsigned char *>(data.data()),
+ data.size());
+
+ uint64_t out1 = rspamd_cryptobox_fast_hash_final(st);
+ CHECK(out1 == 358126267837521635);
+
+ uint64_t out2 = rspamd_cryptobox_fast_hash(reinterpret_cast<const unsigned char *>(data.data()),
+ data.size(), seed);
+ CHECK(out1 == out2);
+
+ rspamd_cryptobox_fast_hash_free(st);
+ }
+
+ TEST_CASE("rspamd_cryptobox_pbkdf")
+ {
+ std::string pass{"passpa"};
+ std::string salt{"salt"};
+
+ uint8_t key1[256] = {0};
+ gsize key_len1 = sizeof(key1);
+
+ uint8_t key2[256] = {0};
+ gsize key_len2 = sizeof(key2);
+
+ unsigned int complexity = 10;
+ enum rspamd_cryptobox_pbkdf_type type = RSPAMD_CRYPTOBOX_PBKDF2;
+
+
+ CHECK(rspamd_cryptobox_pbkdf(pass.data(), pass.size(),
+ reinterpret_cast<const unsigned char *>(salt.data()), salt.size(),
+ key1, key_len1, complexity, type));
+ CHECK(rspamd_cryptobox_pbkdf(pass.data(), pass.size(),
+ reinterpret_cast<const unsigned char *>(salt.data()), salt.size(),
+ key2, key_len2, complexity, type));
+ CHECK(memcmp(key1, key2, key_len1) == 0);
+
+ type = RSPAMD_CRYPTOBOX_CATENA;
+ CHECK(rspamd_cryptobox_pbkdf(pass.data(), pass.size(),
+ reinterpret_cast<const unsigned char *>(salt.data()), salt.size(),
+ key1, key_len1, complexity, type));
+ CHECK(rspamd_cryptobox_pbkdf(pass.data(), pass.size(),
+ reinterpret_cast<const unsigned char *>(salt.data()), salt.size(),
+ key2, key_len2, complexity, type));
+ CHECK(memcmp(key1, key2, key_len1) == 0);
+ }
+
+
+ TEST_CASE("rspamd_cryptobox_encrypt_inplace_25519")
+ {
+ unsigned char data[256];
+ gsize len = sizeof(data);
+ rspamd_nonce_t nonce;
+ rspamd_pk_t pk;
+ rspamd_sk_t sk;
+ rspamd_mac_t sig;
+
+ ottery_rand_bytes(nonce, sizeof(nonce));
+
+ rspamd_cryptobox_keypair(pk, sk);
+
+ memset(sig, 0, sizeof(sig));
+
+ rspamd_cryptobox_encrypt_inplace(data, len, nonce, pk, sk, sig);
+
+ CHECK(rspamd_cryptobox_decrypt_inplace(data, len, nonce, pk, sk, sig));
+ }
+
+ TEST_CASE("rspamd_cryptobox_sign_25519")
+ {
+ rspamd_sig_sk_t sk;
+ rspamd_sig_pk_t pk;
+ unsigned char sig[256];
+ unsigned long long siglen;
+ std::string m{"data to be signed"};
+
+ rspamd_cryptobox_keypair_sig(pk, sk);
+
+ rspamd_cryptobox_sign(sig, &siglen,
+ reinterpret_cast<const unsigned char *>(m.data()), m.size(), sk);
+ bool check_result = rspamd_cryptobox_verify(sig, siglen,
+ reinterpret_cast<const unsigned char *>(m.data()), m.size(),
+ pk);
+ CHECK(check_result == true);
+ }
+
+ TEST_CASE("rspamd_keypair_encryption")
+ {
+ auto *kp = rspamd_keypair_new(RSPAMD_KEYPAIR_KEX);
+ std::string data{"data to be encrypted"};
+ unsigned char *out;
+ gsize outlen;
+ GError *err = nullptr;
+
+ auto ret = rspamd_keypair_encrypt(kp, reinterpret_cast<const unsigned char *>(data.data()), data.size(),
+ &out, &outlen, &err);
+ CHECK(ret);
+ CHECK(err == nullptr);
+
+ unsigned char *decrypted;
+ gsize decrypted_len;
+ ret = rspamd_keypair_decrypt(kp, out, outlen, &decrypted, &decrypted_len, &err);
+ CHECK(ret);
+ CHECK(err == nullptr);
+ CHECK(decrypted_len == data.size());
+ CHECK(data == std::string_view{reinterpret_cast<const char *>(decrypted), decrypted_len});
+
+ g_free(out);
+ g_free(decrypted);
+ }
+}
+
+#endif