From cc994326a05c2ff64ef1e9410e54e1bed3f0fc7c Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Sat, 14 Jan 2023 08:37:22 +0100 Subject: [PATCH] all: cleanups --- .github/workflows/ci.yml | 18 +- .github/workflows/codeql-analysis.yml | 10 +- CMakeLists.txt | 14 +- Makefile | 3 + .../10-create-image.sh | 4 +- .../20-image-run-shell.sh | 2 +- misc/cross-compile-upx-with-podman/Dockerfile | 6 +- .../10-create-image.sh | 2 +- .../20-image-run-shell.sh | 2 +- misc/rebuild-stubs-with-podman/Dockerfile | 7 +- src/Makefile | 2 +- src/{util => check}/dt_check.cpp | 47 +- src/{util => check}/dt_impl.cpp | 5 +- src/check/dt_xspan.cpp | 752 ++++++++++++++++++ src/check/dtx_boost_pfr.cpp | 82 ++ src/check/dtx_rangeless.cpp | 64 ++ src/conf.h | 129 +-- src/util/snprintf.cpp | 18 +- src/util/snprintf.h | 6 +- src/util/xspan.cpp | 723 ----------------- src/util/xspan_impl_common.h | 2 +- 21 files changed, 1030 insertions(+), 868 deletions(-) rename src/{util => check}/dt_check.cpp (94%) rename src/{util => check}/dt_impl.cpp (92%) create mode 100644 src/check/dt_xspan.cpp create mode 100644 src/check/dtx_boost_pfr.cpp create mode 100644 src/check/dtx_rangeless.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36d0cfff..1b0a8fbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,7 +131,7 @@ jobs: matrix: include: - { os: macos-11, gcc: gcc-10, gxx: 'g++-10', testsuite: true } - # { os: macos-12, gcc: gcc-11, gxx: 'g++-11', testsuite: true } # disable gcc - XCode 14.0 ld bug; supposed to be fixed in 14.1 + # { os: macos-12, gcc: gcc-11, gxx: 'g++-11', testsuite: true } # disable gcc - XCode 14.0.1 ld bug; supposed to be fixed in 14.1 - { os: macos-12, testsuite: true } steps: - name: 'Install brew packages' @@ -245,10 +245,12 @@ jobs: fail-fast: false matrix: include: - - { name: amd64-win64-msvc-14.2, os: windows-2019, C: msvc-14.2-x64, A: x64 } # Visual Studio 2019 - - { name: amd64-win64-msvc-14.3, os: windows-2022, C: msvc-14.3-x64, A: x64 } # Visual Studio 2022 - - { name: i386-win32-msvc-14.2, os: windows-2019, C: msvc-14.2-x86, A: x86 } # Visual Studio 2019 - - { name: i386-win32-msvc-14.3, os: windows-2022, C: msvc-14.3-x86, A: x86 } # Visual Studio 2022 + - { name: amd64-win64-vs2019, os: windows-2019, C: msvc-14.2-x64, arch: amd64 } + - { name: amd64-win64-vs2022, os: windows-2022, C: msvc-14.3-x64, arch: amd64 } + - { name: arm64-win64-vs2019, os: windows-2019, C: msvc-14.2-x64, arch: amd64_arm64 } + - { name: arm64-win64-vs2022, os: windows-2022, C: msvc-14.3-x64, arch: amd64_arm64 } + - { name: i386-win32-vs2019, os: windows-2019, C: msvc-14.2-x86, arch: amd64_x86 } + - { name: i386-win32-vs2022, os: windows-2022, C: msvc-14.3-x86, arch: amd64_x86 } steps: - name: 'Check out code' uses: actions/checkout@v3 @@ -263,7 +265,7 @@ jobs: - name: 'Set up Developer Command Prompt' uses: ilammy/msvc-dev-cmd@v1 with: - arch: ${{ matrix.A }} + arch: ${{ matrix.arch }} - name: 'Build' shell: cmd run: | @@ -291,7 +293,7 @@ jobs: set s=%H%\src cat .GITREV.txt set /p GITREV=<.GITREV.txt - cl -std:c++17 -Zc:__cplusplus -EHsc -J -O2 -W4 -WX -DUPX_VERSION_GITREV="""%GITREV%""" -DWITH_ZSTD %DEFS% -I%H%\vendor -I%H%\vendor\boost-pfr\include -Feupx.exe %s%\*.cpp %s%\util\*.cpp %BDIR%\ucl\ucl.lib %BDIR%\zlib\zlib.lib %BDIR%\zstd\zstd.lib /link setargv.obj + cl -std:c++17 -Zc:__cplusplus -EHsc -J -O2 -W4 -WX -DUPX_VERSION_GITREV="""%GITREV%""" -DWITH_ZSTD %DEFS% -I%H%\vendor -I%H%\vendor\boost-pfr\include -Feupx.exe %s%\*.cpp %s%\check\*.cpp %s%\util\*.cpp %BDIR%\ucl\ucl.lib %BDIR%\zlib\zlib.lib %BDIR%\zstd\zstd.lib /link setargv.obj - name: 'Make artifact' shell: bash run: | @@ -306,6 +308,7 @@ jobs: name: ${{ env.artifact_name }} path: tmp/artifact - name: 'Run basic tests' + if: ${{ matrix.arch != 'amd64_arm64' }} run: | $ErrorActionPreference = 'stop' $ErrorView = 'NormalView' @@ -318,6 +321,7 @@ jobs: .\upx.exe -t upx_packed.exe .\upx_packed.exe --version - name: 'Run test suite' + if: ${{ matrix.arch != 'amd64_arm64' }} shell: bash run: | export upx_testsuite_SRCDIR="$(readlink -en ../deps/upx-testsuite)" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ece25149..4658678b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,11 +1,11 @@ name: "CodeQL" on: - push: - branches: [ "devel", "devel4", "devel5", "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "devel", "devel4", "devel5" ] +# push: +# branches: [ "devel", "devel4", "devel5", "master" ] +# pull_request: +# # The branches below must be a subset of the branches above +# branches: [ "devel", "devel4", "devel5" ] schedule: - cron: '20 1 * * 3' workflow_dispatch: diff --git a/CMakeLists.txt b/CMakeLists.txt index 82c8339f..bf194358 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,14 +6,12 @@ if(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") option(UPX_CONFIG_DISABLE_GITREV "Do not compile with default Git version info." ON) option(UPX_CONFIG_DISABLE_SANITIZE "Do not compile with default sanitize options." ON) option(UPX_CONFIG_DISABLE_WERROR "Do not compile with default -Werror option." ON) - option(UPX_CONFIG_DISABLE_ZSTD "Do not compile with zstd; NOTE: zstd is WIP." ON) else() # strict config defaults for devel builds message(STATUS "upx info: strict config defaults enabled") option(UPX_CONFIG_DISABLE_GITREV "Do not compile with default Git version info." OFF) option(UPX_CONFIG_DISABLE_SANITIZE "Do not compile with default sanitize options." OFF) option(UPX_CONFIG_DISABLE_WERROR "Do not compile with default -Werror option." OFF) - option(UPX_CONFIG_DISABLE_ZSTD "Do not compile with zstd; NOTE: zstd is WIP." OFF) endif() # test config options (see below) @@ -42,7 +40,7 @@ set(GITREV_SHORT "") set(GITREV_PLUS "") set(GIT_DESCRIBE "") find_package(Git) -if(GIT_FOUND AND NOT UPX_CONFIG_DISABLE_GITREV) +if(Git_FOUND AND NOT UPX_CONFIG_DISABLE_GITREV) execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --short=12 HEAD RESULT_VARIABLE result ERROR_QUIET @@ -101,6 +99,10 @@ endif() # targets and compilation flags #*********************************************************************** +#find_package(Threads) + +set(UPX_CONFIG_DISABLE_ZSTD ON) # zstd is currently not used; maybe in UPX version 5 + file(GLOB ucl_SOURCES "vendor/ucl/src/*.c") list(SORT ucl_SOURCES) add_library(upx_vendor_ucl STATIC ${ucl_SOURCES}) @@ -118,7 +120,7 @@ add_library(upx_vendor_zstd STATIC ${zstd_SOURCES}) set_property(TARGET upx_vendor_zstd PROPERTY C_STANDARD 11) endif() -file(GLOB upx_SOURCES "src/*.cpp" "src/util/*.cpp") +file(GLOB upx_SOURCES "src/*.cpp" "src/check/*.cpp" "src/util/*.cpp") list(SORT upx_SOURCES) add_executable(upx ${upx_SOURCES}) #target_compile_features(upx PRIVATE cxx_std_17) @@ -223,6 +225,10 @@ if(NOT UPX_CONFIG_DISABLE_ZSTD) target_compile_definitions(${t} PRIVATE WITH_ZSTD=1) target_link_libraries(upx upx_vendor_zstd) endif() +if(Threads_FOUND AND 0) + # for RANGELESS_FN_ENABLE_PARALLEL multithreading test + target_link_libraries(upx Threads::Threads) +endif() #*********************************************************************** # "make test" diff --git a/Makefile b/Makefile index f35192b2..245e5cb0 100644 --- a/Makefile +++ b/Makefile @@ -141,6 +141,9 @@ endif ifeq ($(wildcard ./vendor/lzma-sdk/C/.),) $(error ERROR: missing git submodule; run 'git submodule update --init') endif +ifeq ($(wildcard ./vendor/rangeless/include/.),) + $(error ERROR: missing git submodule; run 'git submodule update --init') +endif ifeq ($(wildcard ./vendor/ucl/include/.),) $(error ERROR: missing git submodule; run 'git submodule update --init') endif diff --git a/misc/cross-compile-upx-with-podman/10-create-image.sh b/misc/cross-compile-upx-with-podman/10-create-image.sh index d4347908..0f24c91c 100755 --- a/misc/cross-compile-upx-with-podman/10-create-image.sh +++ b/misc/cross-compile-upx-with-podman/10-create-image.sh @@ -6,10 +6,10 @@ argv0=$0; argv0abs="$(readlink -fn "$argv0")"; argv0dir="$(dirname "$argv0abs")" # create the image from Dockerfile # using a rootless Podman container -# NOTE: this image is based on rebuild-stubs-with-upx/upx-stubtools-20221212-v2, +# NOTE: this image is based on rebuild-stubs-with-upx/upx-stubtools-20221212-v3, # so you have to create that image first # WARNING: we install many packages, so the resulting image needs A LOT of disk space! -image=upx-cross-compile-20221212-v2 +image=upx-cross-compile-20230115-v1 podman build -t "$image" -f "$argv0dir/Dockerfile" "$argv0dir" diff --git a/misc/cross-compile-upx-with-podman/20-image-run-shell.sh b/misc/cross-compile-upx-with-podman/20-image-run-shell.sh index 051db536..e19c8bac 100755 --- a/misc/cross-compile-upx-with-podman/20-image-run-shell.sh +++ b/misc/cross-compile-upx-with-podman/20-image-run-shell.sh @@ -6,7 +6,7 @@ argv0=$0; argv0abs="$(readlink -fn "$argv0")"; argv0dir="$(dirname "$argv0abs")" # run an interactive shell in the image # using a rootless Podman container -image=upx-cross-compile-20221212-v2 +image=upx-cross-compile-20230115-v1 flags=( -ti --read-only --rm ) flags+=( --cap-drop=all ) # drop all capabilities diff --git a/misc/cross-compile-upx-with-podman/Dockerfile b/misc/cross-compile-upx-with-podman/Dockerfile index 79b3ed58..7c2fd779 100644 --- a/misc/cross-compile-upx-with-podman/Dockerfile +++ b/misc/cross-compile-upx-with-podman/Dockerfile @@ -1,8 +1,8 @@ -# NOTE: this image is based on rebuild-stubs-with-upx/upx-stubtools-20221212-v2, +# NOTE: this image is based on rebuild-stubs-with-upx/upx-stubtools-20221212-v3, # so you have to create that image first # WARNING: we install many packages, so the resulting image needs A LOT of disk space! -FROM localhost/upx-stubtools-20221212-v2 -ENV UPX_CONTAINER_IMAGE_NAME=upx-cross-compile-20221212-v2 +FROM localhost/upx-stubtools-20221212-v3 +ENV UPX_CONTAINER_IMAGE_NAME=upx-cross-compile-20230115-v1 ARG DEBIAN_FRONTEND=noninteractive USER root diff --git a/misc/rebuild-stubs-with-podman/10-create-image.sh b/misc/rebuild-stubs-with-podman/10-create-image.sh index ef61ce62..0c24cbb7 100755 --- a/misc/rebuild-stubs-with-podman/10-create-image.sh +++ b/misc/rebuild-stubs-with-podman/10-create-image.sh @@ -6,7 +6,7 @@ argv0=$0; argv0abs="$(readlink -fn "$argv0")"; argv0dir="$(dirname "$argv0abs")" # create the image from Dockerfile # using a rootless Podman container -image=upx-stubtools-20221212-v2 +image=upx-stubtools-20221212-v3 podman build -t "$image" -f "$argv0dir/Dockerfile" "$argv0dir" diff --git a/misc/rebuild-stubs-with-podman/20-image-run-shell.sh b/misc/rebuild-stubs-with-podman/20-image-run-shell.sh index 222eacf0..e96fa38a 100755 --- a/misc/rebuild-stubs-with-podman/20-image-run-shell.sh +++ b/misc/rebuild-stubs-with-podman/20-image-run-shell.sh @@ -6,7 +6,7 @@ argv0=$0; argv0abs="$(readlink -fn "$argv0")"; argv0dir="$(dirname "$argv0abs")" # run an interactive shell in the image # using a rootless Podman container -image=upx-stubtools-20221212-v2 +image=upx-stubtools-20221212-v3 flags=( -ti --read-only --rm ) flags+=( --cap-drop=all ) # drop all capabilities diff --git a/misc/rebuild-stubs-with-podman/Dockerfile b/misc/rebuild-stubs-with-podman/Dockerfile index 2dcd56cb..5fa5265a 100644 --- a/misc/rebuild-stubs-with-podman/Dockerfile +++ b/misc/rebuild-stubs-with-podman/Dockerfile @@ -1,5 +1,5 @@ FROM docker.io/library/ubuntu:22.04 -ENV UPX_CONTAINER_IMAGE_NAME=upx-stubtools-20221212-v2 +ENV UPX_CONTAINER_IMAGE_NAME=upx-stubtools-20221212-v3 ARG DEBIAN_FRONTEND=noninteractive ENV LANG=C.UTF-8 @@ -13,8 +13,9 @@ RUN dpkg --add-architecture i386 \ # the following packages are not required for rebuilding the stubs, but # they do make the image much more convenient and also allow building # the full UPX binary inside the container via CMake: - 7zip bzip2 cmake elfutils file g++ gdb htop libzstd-dev lzip lzop ninja-build \ - p7zip patch patchelf pax-utils rsync screen unzip vim zip zlib1g-dev zsh zstd \ + 7zip bfs bzip2 cmake elfutils fd-find file fzf g++ gdb htop hyperfine \ + libzstd-dev lzip lzop ninja-build p7zip patch patchelf pax-utils ripgrep \ + rsync screen universal-ctags unzip vim zip zlib1g-dev zsh zstd \ # extra packages for compiling with "gcc -m32" and and "gcc -mx32": gcc-multilib g++-multilib \ && true diff --git a/src/Makefile b/src/Makefile index 2925c415..79d4e06d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -79,7 +79,7 @@ CLANG_FORMAT_EXCLUDE_FILES += conf.h miniacc.h version.h CLANG_FORMAT_EXCLUDE_FILES += compress.cpp compress.h filter.cpp filter.h filteri.cpp help.cpp CLANG_FORMAT_EXCLUDE_FILES += p_elf.h p_elf_enum.h p_lx_% p_mach% p_unix% p_vmli% CLANG_FORMAT_EXCLUDE_FILES += p_w32pe.cpp p_w64pep.cpp packer_c.cpp packer_f.cpp pefile% -CLANG_FORMAT_FILES := $(sort $(wildcard *.[ch]* ../maint/src/*.[ch]* util/*.[ch]*)) +CLANG_FORMAT_FILES := $(sort $(wildcard *.[ch]* ../maint/src/*.[ch]* check/*.[ch]* util/*.[ch]*)) CLANG_FORMAT_FILES := $(filter-out $(CLANG_FORMAT_EXCLUDE_FILES),$(CLANG_FORMAT_FILES)) clang-format: PHONY $(CLANG_FORMAT_FILES) @echo "running upx-clang-format" diff --git a/src/util/dt_check.cpp b/src/check/dt_check.cpp similarity index 94% rename from src/util/dt_check.cpp rename to src/check/dt_check.cpp index 1fefbb9c..94f14b64 100644 --- a/src/util/dt_check.cpp +++ b/src/check/dt_check.cpp @@ -3,7 +3,6 @@ This file is part of the UPX executable compressor. Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer - Copyright (C) 1996-2023 Laszlo Molnar All Rights Reserved. UPX and the UCL library are free software; you can redistribute them @@ -21,15 +20,10 @@ If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - Markus F.X.J. Oberhumer Laszlo Molnar - + Markus F.X.J. Oberhumer + */ -#if DEBUG || 1 -#ifndef WITH_BOOST_PFR -#define WITH_BOOST_PFR 1 -#endif -#endif #include "../conf.h" /************************************************************************* @@ -138,8 +132,13 @@ namespace { template struct TestBELE { __acc_static_noinline bool test(void) { - COMPILE_TIME_ASSERT_ALIGNED1(T) + // POD checks + // COMPILE_TIME_ASSERT(std::is_pod::value); // deprecated in C++20 + COMPILE_TIME_ASSERT(std::is_standard_layout::value); + COMPILE_TIME_ASSERT(std::is_trivial::value); + // alignment checks { + COMPILE_TIME_ASSERT_ALIGNED1(T) struct alignas(1) test1_t { char a; T b; @@ -160,6 +159,7 @@ struct TestBELE { UNUSED(t2); } #if 1 + // arithmetic checks { T allbits; allbits = 0; @@ -453,33 +453,4 @@ TEST_CASE("libc snprintf") { CHECK_EQ(strcmp(buf, "-7.0.0.0.0.0.0.0.7.0xffffffffffffffff"), 0); } -#if WITH_BOOST_PFR -TEST_CASE("Boost.PFR") { - int i = -1; - CHECK_EQ(strcmp(pfr_str(i), "-1"), 0); - BE32 b32; - b32 = 1; - LE32 l32; - l32 = 2; - CHECK_EQ(strcmp(pfr_str(b32), "1"), 0); - CHECK_EQ(strcmp(pfr_str(l32), "2"), 0); - struct Foo { - BE16 b16; - BE32 b32; - BE64 b64; - LE16 l16; - LE32 l32; - LE64 l64; - }; - Foo foo; - foo.b16 = 1; - foo.b32 = 2; - foo.b64 = 3; - foo.l16 = 4; - foo.l32 = 5; - foo.l64 = 6; - CHECK_EQ(strcmp(pfr_str("foo", "=", foo), "foo = {1, 2, 3, 4, 5, 6}"), 0); -} -#endif // WITH_BOOST_PFR - /* vim:set ts=4 sw=4 et: */ diff --git a/src/util/dt_impl.cpp b/src/check/dt_impl.cpp similarity index 92% rename from src/util/dt_impl.cpp rename to src/check/dt_impl.cpp index 866db9c7..a684d6f7 100644 --- a/src/util/dt_impl.cpp +++ b/src/check/dt_impl.cpp @@ -3,7 +3,6 @@ This file is part of the UPX executable compressor. Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer - Copyright (C) 1996-2023 Laszlo Molnar All Rights Reserved. UPX and the UCL library are free software; you can redistribute them @@ -21,8 +20,8 @@ If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - Markus F.X.J. Oberhumer Laszlo Molnar - + Markus F.X.J. Oberhumer + */ /************************************************************************* diff --git a/src/check/dt_xspan.cpp b/src/check/dt_xspan.cpp new file mode 100644 index 00000000..63ce4dcc --- /dev/null +++ b/src/check/dt_xspan.cpp @@ -0,0 +1,752 @@ +/* xspan -- a minimally invasive checked memory smart pointer + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +// lots of tests (and probably quite a number of redundant tests) + +#include "../conf.h" + +/************************************************************************* +// basic +**************************************************************************/ + +TEST_CASE("basic xspan usage") { + char buf[4] = {0, 1, 2, 3}; + + SUBCASE("SPAN_x") { + SPAN_0(char) a0 = nullptr; + + SPAN_0(char) b0 = buf; + SPAN_P(char) bp = buf; + + SPAN_0(char) c0 = SPAN_0_MAKE(char, buf); + SPAN_P(char) cp = SPAN_P_MAKE(char, buf); + SPAN_S(char) cs = SPAN_S_MAKE(char, buf, sizeof(buf)); + + SPAN_0(const char) const x0 = SPAN_0_MAKE(const char, buf); + SPAN_P(const char) const xp = SPAN_P_MAKE(const char, buf); + SPAN_S(const char) const xs = SPAN_S_MAKE(const char, buf, sizeof(buf)); + SPAN_P(const char) const yp = xs; + SPAN_0(const char) const z0p = yp; + SPAN_0(const char) const z0s = xs; + + CHECK((a0 == nullptr)); + CHECK(c0 == b0); + CHECK(cp == bp); + CHECK(cs == bp); + CHECK(x0 == z0p); + CHECK(xp == z0s); + + CHECK(raw_bytes(c0, 4) == buf); + CHECK(raw_index_bytes(c0, 1, 3) == buf + 1); + CHECK(raw_bytes(cp, 4) == buf); + CHECK(raw_index_bytes(cp, 1, 3) == buf + 1); + CHECK(raw_bytes(cs, 4) == buf); + CHECK(raw_index_bytes(cs, 1, 3) == buf + 1); + CHECK_THROWS(raw_bytes(cs, 5)); + CHECK_THROWS(raw_index_bytes(cs, 1, 4)); + } + + SUBCASE("SPAN_x_VAR") { + SPAN_0_VAR(char, a0, nullptr); + + SPAN_0_VAR(char, b0, buf); + SPAN_P_VAR(char, bp, buf); + + SPAN_0_VAR(char, c0, buf, sizeof(buf)); + SPAN_P_VAR(char, cp, buf, sizeof(buf)); + SPAN_S_VAR(char, cs, buf, sizeof(buf)); + + SPAN_0_VAR(char, d0, buf + 1, sizeof(buf), buf); + SPAN_P_VAR(char, dp, buf + 1, sizeof(buf), buf); + SPAN_S_VAR(char, ds, buf + 1, sizeof(buf), buf); + + SPAN_0_VAR(const char, const x0, buf, sizeof(buf)); + SPAN_P_VAR(const char, const xp, buf, sizeof(buf)); + SPAN_S_VAR(const char, const xs, buf, sizeof(buf)); + SPAN_P_VAR(const char, const yp, xs); + SPAN_0_VAR(const char, const z0p, yp); + SPAN_0_VAR(const char, const z0s, xs); + + CHECK((a0 == nullptr)); + CHECK(c0 == b0); + CHECK(cp == bp); + CHECK(cs == bp); + CHECK(d0 == dp); + CHECK(d0 == ds); + CHECK(x0 == z0p); + CHECK(xp == z0s); + + CHECK(raw_bytes(c0, 4) == buf); + CHECK(raw_index_bytes(c0, 1, 3) == buf + 1); + CHECK(raw_bytes(cp, 4) == buf); + CHECK(raw_index_bytes(cp, 1, 3) == buf + 1); + CHECK(raw_bytes(cs, 4) == buf); + CHECK(raw_index_bytes(cs, 1, 3) == buf + 1); + CHECK_THROWS(raw_bytes(cs, 5)); + CHECK_THROWS(raw_index_bytes(cs, 1, 4)); + } + + SUBCASE("xspan in class") { + struct MyType { + SPAN_0(char) s0; + SPAN_P(char) sp; + SPAN_S(char) ss; +#if __cplusplus >= 201103L + SPAN_0(char) x0 = nullptr; +#endif +#if WITH_SPAN >= 2 + // much nicer syntax when using fully checked xspan: + MyType(char *b, size_t n, bool) : s0(b, n), sp(b, n), ss(b, n) {} +#endif + MyType(char *b, size_t n) + : s0(SPAN_0_MAKE(char, b, n)), sp(SPAN_P_MAKE(char, b, n)), + ss(SPAN_S_MAKE(char, b, n)) { + UNUSED(n); + } + }; + MyType x(buf, sizeof(buf)); + MyType y = MyType(buf, sizeof(buf)); + CHECK(x.s0 == y.sp); + } +} + +/************************************************************************* +// +**************************************************************************/ + +#if (WITH_SPAN >= 2) && DEBUG + +TEST_CASE("PtrOrSpanOrNull") { + char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; + char *base_buf = real_buf + 2; + char *const my_null = nullptr; + typedef PtrOrSpanOrNull Span0; + + // basic nullptr + CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = my_null); + CHECK_NOTHROW(Span0(base_buf, 4, base_buf).assign(my_null)); + // basic range checking + CHECK_NOTHROW(Span0(base_buf, 4, base_buf)); + CHECK_NOTHROW(Span0(base_buf, 0, base_buf)); + CHECK_NOTHROW(Span0(base_buf, 0, base_buf) - 0); + CHECK_THROWS(Span0(base_buf, 0, base_buf) + 1); + CHECK_THROWS(Span0(base_buf, 0, base_buf) - 1); + CHECK_NOTHROW(Span0(base_buf, 4, base_buf) + 4); + CHECK_THROWS(Span0(base_buf, 4, base_buf) + 5); + CHECK_THROWS(Span0(base_buf - 1, 4, base_buf)); + CHECK_THROWS(Span0(base_buf + 1, 0, base_buf)); + // basic same base + CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 3, base_buf)); + CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 1, base_buf)); + CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 5, base_buf)); + CHECK_THROWS(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 3, base_buf + 1)); + + Span0 a1(nullptr); + assert(a1 == nullptr); + assert(a1.raw_ptr() == nullptr); + assert(a1.raw_base() == nullptr); + assert(a1.raw_size_in_bytes() == 0u); + CHECK_THROWS(*a1); + CHECK_THROWS(a1[0]); + + Span0 a2 = nullptr; + assert(a2 == nullptr); + assert(a2.raw_ptr() == nullptr); + assert(a2.raw_base() == nullptr); + assert(a2.raw_size_in_bytes() == 0u); + CHECK_THROWS(*a2); + CHECK_THROWS(a2[0]); + + Span0 base0(nullptr, 4, base_buf); + assert(base0.raw_ptr() == nullptr); + assert(base0.raw_base() == base_buf); + assert(base0.raw_size_in_bytes() == 4u); + CHECK_THROWS(*base0); // nullptr + CHECK_THROWS(base0[0]); // nullptr + CHECK_THROWS(base0 + 1); // nullptr + + Span0 base4(base_buf, 4); + assert(base4.raw_ptr() == base_buf); + assert(base4.raw_base() == base_buf); + assert(base4.raw_size_in_bytes() == 4u); + + a1 = base_buf; + a1 = base0; + assert(a1 == nullptr); + assert(a1.raw_ptr() == nullptr); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + a1 = base4; + assert(a1 == base_buf); + assert(a1.raw_ptr() == base_buf); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + + a1 = base_buf; + assert(a1 != nullptr); + a1 = base_buf + 1; + CHECK(*a1++ == 1); + CHECK(*++a1 == 3); + CHECK(*a1 == 3); + a1 = base_buf + 4; // at the end of buffer + CHECK_THROWS(*a1); + CHECK_THROWS(a1 = base_buf + 5); // range error + assert(a1 == base_buf + 4); + CHECK(a1[-4] == 0); + CHECK_THROWS(a1[-5]); // range error + a1 = base_buf; + CHECK(*a1 == 0); + + Span0 new_base4(base_buf + 2, 4); + CHECK_THROWS(a1 = new_base4); // not same base + a2 = new_base4; + CHECK_THROWS(a2 = base4); // not same base + + Span0 s0_no_base(nullptr); + Span0 s0_with_base(nullptr, 4, base_buf); + s0_no_base = nullptr; + s0_with_base = nullptr; + s0_with_base = s0_no_base; + assert(s0_no_base.raw_base() == nullptr); + assert(s0_with_base.raw_base() == base_buf); + s0_no_base = s0_with_base; + assert(s0_no_base.raw_base() == base_buf); + assert(s0_no_base.raw_ptr() == nullptr); + assert(s0_with_base.raw_ptr() == nullptr); + s0_no_base = my_null; + s0_with_base = my_null; +} + +/************************************************************************* +// +**************************************************************************/ + +TEST_CASE("PtrOrSpan") { + char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; + char *base_buf = real_buf + 2; + char *const my_null = nullptr; + typedef PtrOrSpan SpanP; + + // basic nullptr + CHECK_THROWS(SpanP(base_buf, 4, base_buf) = my_null); + CHECK_THROWS(SpanP(base_buf, 4, base_buf).assign(my_null)); + // basic range checking + CHECK_NOTHROW(SpanP(base_buf, 4, base_buf)); + CHECK_NOTHROW(SpanP(base_buf, 0, base_buf)); + CHECK_NOTHROW(SpanP(base_buf, 0, base_buf) - 0); + CHECK_THROWS(SpanP(base_buf, 0, base_buf) + 1); + CHECK_THROWS(SpanP(base_buf, 0, base_buf) - 1); + CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) + 4); + CHECK_THROWS(SpanP(base_buf, 4, base_buf) + 5); + CHECK_THROWS(SpanP(base_buf - 1, 4, base_buf)); + CHECK_THROWS(SpanP(base_buf + 1, 0, base_buf)); + // basic same base + CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 3, base_buf)); + CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 1, base_buf)); + CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 5, base_buf)); + CHECK_THROWS(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 3, base_buf + 1)); + + SpanP x1(base_buf, 0); + assert(x1 != nullptr); + assert(x1.raw_ptr() == base_buf); + assert(x1.raw_base() == base_buf); + assert(x1.raw_size_in_bytes() == 0u); + CHECK_THROWS(*x1); + CHECK_THROWS(x1[0]); + + SpanP a2 = base_buf; + assert(a2 != nullptr); + assert(a2.raw_ptr() == base_buf); + assert(a2.raw_base() == nullptr); + assert(a2.raw_size_in_bytes() == 0u); + CHECK(*a2 == 0); + CHECK(a2[1] == 1); + + SpanP base0(base_buf, 4, base_buf); + assert(base0.raw_ptr() == base_buf); + assert(base0.raw_base() == base_buf); + assert(base0.raw_size_in_bytes() == 4u); + + SpanP base4(base_buf, 4); + assert(base4.raw_ptr() == base_buf); + assert(base4.raw_base() == base_buf); + assert(base4.raw_size_in_bytes() == 4u); + + SpanP a1(base_buf, 4); + a1 = base_buf; + a1 = base0; + assert(a1 == base0); + assert(a1 != nullptr); + assert(a1.raw_ptr() == base0.raw_ptr()); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + a1 = base4; + assert(a1 == base_buf); + assert(a1.raw_ptr() == base_buf); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + + a1 = base_buf; + a1 = base_buf + 1; + CHECK(*a1++ == 1); + CHECK(*++a1 == 3); + CHECK(*a1 == 3); + a1 = base_buf + 4; // at the end of buffer + CHECK_THROWS(*a1); + CHECK_THROWS(a1 = base_buf + 5); // range error + assert(a1 == base_buf + 4); + CHECK(a1[-4] == 0); + CHECK_THROWS(a1[-5]); // range error + a1 = base_buf; + CHECK(*a1 == 0); + + SpanP new_base4(base_buf + 2, 4); + CHECK_THROWS(a1 = new_base4); // not same base + a2 = new_base4; + CHECK_THROWS(a2 = base4); // not same base + + SpanP sp_no_base(base_buf); + SpanP sp_with_base(base_buf, 4, base_buf); + assert(sp_no_base.raw_base() == nullptr); + assert(sp_with_base.raw_base() == base_buf); + CHECK_THROWS(sp_no_base = my_null); // nullptr assignment + CHECK_THROWS(sp_with_base = my_null); // nullptr assignment +#if SPAN_CONFIG_ENABLE_SPAN_CONVERSION + typedef PtrOrSpanOrNull Span0; + Span0 s0_no_base(nullptr); + Span0 s0_with_base(nullptr, 4, base_buf); + CHECK_THROWS(sp_no_base = s0_no_base); // nullptr assignment + CHECK_THROWS(sp_no_base = s0_with_base); // nullptr assignment + CHECK_THROWS(sp_with_base = s0_no_base); // nullptr assignment + CHECK_THROWS(sp_with_base = s0_with_base); // nullptr assignment +#endif +} + +/************************************************************************* +// +**************************************************************************/ + +TEST_CASE("Span") { + char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; + char *base_buf = real_buf + 2; + char *const my_null = nullptr; + typedef Span SpanS; + + // basic nullptr + CHECK_THROWS(SpanS(base_buf, 4, base_buf) = my_null); + CHECK_THROWS(SpanS(base_buf, 4, base_buf).assign(my_null)); + // basic range checking + CHECK_NOTHROW(SpanS(base_buf, 4, base_buf)); + CHECK_NOTHROW(SpanS(base_buf, 0, base_buf)); + CHECK_NOTHROW(SpanS(base_buf, 0, base_buf) - 0); + CHECK_THROWS(SpanS(base_buf, 0, base_buf) + 1); + CHECK_THROWS(SpanS(base_buf, 0, base_buf) - 1); + CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) + 4); + CHECK_THROWS(SpanS(base_buf, 4, base_buf) + 5); + CHECK_THROWS(SpanS(base_buf - 1, 4, base_buf)); + CHECK_THROWS(SpanS(base_buf + 1, 0, base_buf)); + // basic same base + CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 3, base_buf)); + CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 1, base_buf)); + CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 5, base_buf)); + CHECK_THROWS(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 3, base_buf + 1)); + + SpanS x1(base_buf, 0); + assert(x1 != nullptr); + assert(x1.raw_ptr() == base_buf); + assert(x1.raw_base() == base_buf); + assert(x1.raw_size_in_bytes() == 0u); + CHECK_THROWS(*x1); + CHECK_THROWS(x1[0]); + + SpanS a2(base_buf, 4); + assert(a2 != nullptr); + assert(a2.raw_ptr() == base_buf); + assert(a2.raw_base() == base_buf); + assert(a2.raw_size_in_bytes() == 4u); + CHECK(*a2 == 0); + CHECK(a2[1] == 1); + + SpanS base0(base_buf, 4, base_buf); + assert(base0.raw_ptr() == base_buf); + assert(base0.raw_base() == base_buf); + assert(base0.raw_size_in_bytes() == 4u); + + SpanS base4(base_buf, 4); + assert(base4.raw_ptr() == base_buf); + assert(base4.raw_base() == base_buf); + assert(base4.raw_size_in_bytes() == 4u); + + SpanS a1(base_buf, 4); + a1 = base_buf; + a1 = base0; + assert(a1 == base0); + assert(a1 != nullptr); + assert(a1.raw_ptr() == base0.raw_ptr()); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + a1 = base4; + assert(a1 == base_buf); + assert(a1.raw_ptr() == base_buf); + assert(a1.raw_base() == base_buf); + assert(a1.raw_size_in_bytes() == 4u); + + a1 = base_buf; + a1 = base_buf + 1; + CHECK(*a1++ == 1); + CHECK(*++a1 == 3); + CHECK(*a1 == 3); + a1 = base_buf + 4; // at the end of buffer + CHECK_THROWS(*a1); + CHECK_THROWS(a1 = base_buf + 5); // range error + assert(a1 == base_buf + 4); + CHECK(a1[-4] == 0); + CHECK_THROWS(a1[-5]); // range error + a1 = base_buf; + CHECK(*a1 == 0); + + SpanS new_base4(base_buf + 2, 4); + CHECK_THROWS(a1 = new_base4); // not same base + CHECK_THROWS(a2 = new_base4); // not same base + + SpanS ss_with_base(base_buf, 4, base_buf); + assert(ss_with_base.raw_base() == base_buf); + CHECK_THROWS(ss_with_base = my_null); // nullptr assignment +#if SPAN_CONFIG_ENABLE_SPAN_CONVERSION + { + typedef PtrOrSpanOrNull Span0; + // v0 nullptr, b0 base, b1 base + 1 + const Span0 v0_v0(nullptr); + const Span0 v0_b0(nullptr, 4, base_buf); + const Span0 v0_b1(nullptr, 3, base_buf + 1); + const Span0 b0_v0(base_buf); + const Span0 b0_b0(base_buf, 4, base_buf); + CHECK_THROWS(SPAN_0_MAKE(char, base_buf, 3, base_buf + 1)); // b0_b1 + const Span0 b1_v0(base_buf + 1); + const Span0 b1_b0(base_buf + 1, 4, base_buf); + const Span0 b1_b1(base_buf + 1, 3, base_buf + 1); + CHECK_THROWS(ss_with_base = v0_v0); // nullptr assignment + CHECK_THROWS(ss_with_base = v0_b0); // nullptr assignment + CHECK_THROWS(ss_with_base = v0_b1); // nullptr assignment + CHECK_NOTHROW(ss_with_base = b0_v0); + CHECK_NOTHROW(ss_with_base = b0_b0); + CHECK_NOTHROW(ss_with_base = b1_v0); + CHECK_NOTHROW(ss_with_base = b1_b0); + CHECK_THROWS(ss_with_base = b1_b1); // different base + CHECK_THROWS(SPAN_S_MAKE(char, v0_v0)); + CHECK_THROWS(SPAN_S_MAKE(char, v0_b0)); + CHECK_THROWS(SPAN_S_MAKE(char, v0_b1)); + CHECK_THROWS(SPAN_S_MAKE(char, b0_v0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b0_b0)); + CHECK_THROWS(SPAN_S_MAKE(char, b1_v0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b1)); + // + CHECK((SPAN_S_MAKE(char, b0_b0).raw_base() == base_buf)); + CHECK((SPAN_S_MAKE(char, b1_b0).raw_base() == base_buf)); + CHECK((SPAN_S_MAKE(char, b1_b1).raw_base() == base_buf + 1)); + } + { + typedef PtrOrSpan SpanP; + // v0 nullptr, b0 base, b1 base + 1 + const SpanP b0_v0(base_buf); + const SpanP b0_b0(base_buf, 4, base_buf); + CHECK_THROWS(SPAN_P_MAKE(char, base_buf, 3, base_buf + 1)); // b0_b1 + const SpanP b1_v0(base_buf + 1); + const SpanP b1_b0(base_buf + 1, 4, base_buf); + const SpanP b1_b1(base_buf + 1, 3, base_buf + 1); + CHECK_NOTHROW(ss_with_base = b0_v0); + CHECK_NOTHROW(ss_with_base = b0_b0); + CHECK_NOTHROW(ss_with_base = b1_v0); + CHECK_NOTHROW(ss_with_base = b1_b0); + CHECK_THROWS(ss_with_base = b1_b1); // different base + CHECK_THROWS(SPAN_S_MAKE(char, b0_v0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b0_b0)); + CHECK_THROWS(SPAN_S_MAKE(char, b1_v0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b0)); + CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b1)); + // + CHECK((SPAN_S_MAKE(char, b0_b0).raw_base() == base_buf)); + CHECK((SPAN_S_MAKE(char, b1_b0).raw_base() == base_buf)); + CHECK((SPAN_S_MAKE(char, b1_b1).raw_base() == base_buf + 1)); + } +#endif +} + +/************************************************************************* +// +**************************************************************************/ + +TEST_CASE("Span void ptr") { + static char a[4] = {0, 1, 2, 3}; + SPAN_0(void) a0(a, 4); + SPAN_P(void) ap(a, 4); + SPAN_S(void) as(a, 4); + SPAN_0(const void) c0(a, 4); + SPAN_P(const void) cp(a, 4); + SPAN_S(const void) cs(a, 4); + static const char b[4] = {0, 1, 2, 3}; + SPAN_0(const void) b0(b, 4); + SPAN_P(const void) bp(b, 4); + SPAN_S(const void) bs(b, 4); +} + +TEST_CASE("Span deref/array/arrow") { + static char real_a[2 + 4 + 2] = {126, 127, 0, 1, 2, 3, 124, 125}; + static char *a = real_a + 2; + SPAN_0(char) a0(a, 4); + SPAN_P(char) ap(a, 4); + SPAN_S(char) as(a, 4); + CHECK_THROWS(a0[4]); + CHECK_THROWS(a0[-1]); + CHECK_THROWS(a0[-2]); + a0 += 2; + CHECK(*a0 == 2); + CHECK(a0[-1] == 1); + CHECK(a0[0] == 2); + CHECK(a0[1] == 3); + ap += 2; + CHECK(*ap == 2); + CHECK(ap[-1] == 1); + CHECK(ap[0] == 2); + CHECK(ap[1] == 3); + as += 2; + CHECK(*as == 2); + CHECK(as[-1] == 1); + CHECK(as[0] == 2); + CHECK(as[1] == 3); +} + +TEST_CASE("Span subspan") { + static char buf[4] = {0, 1, 2, 3}; + SPAN_S(char) as(buf, 4); + CHECK(as.subspan(1, 1)[0] == 1); + CHECK((as + 1).subspan(1, 1)[0] == 2); + CHECK((as + 2).subspan(0, -2)[0] == 0); + CHECK_THROWS(as.subspan(1, 0)[0]); + CHECK_THROWS(as.subspan(1, 1)[-1]); +} + +TEST_CASE("Span constness") { + static char buf[4] = {0, 1, 2, 3}; + + SPAN_0(char) b0(buf, 4); + SPAN_P(char) bp(buf, 4); + SPAN_S(char) bs(buf, 4); + + SPAN_0(char) s0(b0); + SPAN_P(char) sp(bp); + SPAN_S(char) ss(bs); + + SPAN_0(const char) b0c(buf, 4); + SPAN_P(const char) bpc(buf, 4); + SPAN_S(const char) bsc(buf, 4); + + SPAN_0(const char) s0c(b0c); + SPAN_P(const char) spc(bpc); + SPAN_S(const char) ssc(bsc); + + SPAN_0(const char) x0c(b0); + SPAN_P(const char) xpc(bp); + SPAN_S(const char) xsc(bs); + + CHECK(ptr_diff_bytes(b0, buf) == 0); + CHECK(ptr_diff_bytes(bp, buf) == 0); + CHECK(ptr_diff_bytes(bs, buf) == 0); + CHECK(ptr_diff_bytes(s0, buf) == 0); + CHECK(ptr_diff_bytes(sp, buf) == 0); + CHECK(ptr_diff_bytes(bs, buf) == 0); + // + CHECK(ptr_diff_bytes(s0, bp) == 0); + CHECK(ptr_diff_bytes(s0, sp) == 0); + CHECK(ptr_diff_bytes(s0, ss) == 0); + // + CHECK(ptr_diff_bytes(s0c, b0c) == 0); + CHECK(ptr_diff_bytes(spc, bpc) == 0); + CHECK(ptr_diff_bytes(ssc, bsc) == 0); +} + +/************************************************************************* +// +**************************************************************************/ + +#if !defined(DOCTEST_CONFIG_DISABLE) +namespace { +int my_memcmp_v1(SPAN_P(const void) a, SPAN_0(const void) b, size_t n) { + if (b == nullptr) + return -2; + SPAN_0(const void) x(a); + return memcmp(x, b, n); +} +int my_memcmp_v2(SPAN_P(const char) a, SPAN_0(const char) b, size_t n) { + if (a == b) + return 0; + if (b == nullptr) + return -2; + a += 1; + b -= 1; + SPAN_0(const char) x(a); + SPAN_0(const char) y = b; + return memcmp(x, y, n); +} +} // namespace +#endif + +TEST_CASE("PtrOrSpan") { + static const char buf[4] = {0, 1, 2, 3}; + CHECK(my_memcmp_v1(buf, nullptr, 4) == -2); + CHECK(my_memcmp_v2(buf + 4, buf + 4, 999) == 0); + CHECK(my_memcmp_v2(buf, buf + 2, 3) == 0); +} + +/************************************************************************* +// +**************************************************************************/ + +TEST_CASE("PtrOrSpan char") { + char real_buf[2 + 8 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 6, 7, 124, 125}; + char *buf = real_buf + 2; + SPAN_P(char) a(buf, SpanSizeInBytes(8)); + SPAN_P(char) b = a.subspan(0, 7); + SPAN_P(char) c = (b + 1).subspan(0, 6); + a += 1; + CHECK(*a == 1); + *a++ += 1; + *b++ = 1; + CHECK(a == buf + 2); + CHECK(b == buf + 1); + CHECK(c == buf + 1); + CHECK(*b == 2); + CHECK(*c == 2); + CHECK(a.raw_size_in_bytes() == 8u); + CHECK(b.raw_size_in_bytes() == 7u); + CHECK(c.raw_size_in_bytes() == 6u); + CHECK(a.raw_base() == buf); + CHECK(b.raw_base() == buf); + CHECK(c.raw_base() == buf + 1); +#ifdef UPX_VERSION_HEX + CHECK(get_le32(a) != 0); +#endif + ++c; + c++; +#ifdef UPX_VERSION_HEX + CHECK(get_le32(c) != 0); +#endif + ++c; +#ifdef UPX_VERSION_HEX + CHECK_THROWS(get_le32(c)); +#endif + ++b; + b++; + b += 4; + CHECK(b.raw_ptr() == buf + 7); + CHECK_THROWS(*b); + CHECK(a.raw_size_in_bytes() == 8u); + a = b; + CHECK(a.raw_size_in_bytes() == 8u); + CHECK(a.raw_ptr() == buf + 7); + a++; + CHECK_THROWS(*a); + CHECK_THROWS(raw_bytes(a, 1)); + a = b; + CHECK_THROWS(a = c); + *a = 0; + a = buf; +#ifdef UPX_VERSION_HEX + CHECK(upx_safe_strlen(a) == 7u); +#endif +} + +TEST_CASE("PtrOrSpan int") { + int buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + SPAN_P(int) a(buf, SpanCount(8)); + CHECK(a.raw_size_in_bytes() == 8 * sizeof(int)); + SPAN_P(int) b = a.subspan(0, 7); + CHECK(b.raw_size_in_bytes() == 7 * sizeof(int)); + SPAN_P(int) c = (b + 1).subspan(0, 6); + CHECK(c.raw_size_in_bytes() == 6 * sizeof(int)); + a += 1; + CHECK(*a == 1); + CHECK(*a++ == 1); + CHECK(*++a == 3); + CHECK(--*a == 2); + CHECK(*a-- == 2); + CHECK(*b == 0); + CHECK(*c == 1); + a = buf + 7; +#ifdef UPX_VERSION_HEX + CHECK(get_le32(a) == ne32_to_le32(7)); +#endif + a++; +#ifdef UPX_VERSION_HEX + CHECK_THROWS(get_le32(a)); +#endif + CHECK_THROWS(raw_bytes(a, 1)); +} + +/************************************************************************* +// codegen +**************************************************************************/ + +namespace { +template +__acc_static_noinline int foo(T p) { + unsigned r = 0; + r += *p++; + r += *++p; + p += 3; + r += *p; + return r; +} + +template +SPAN_0(T) +make_span_0(T *ptr, size_t count) { + return PtrOrSpanOrNull(ptr, count); +} +template +SPAN_P(T) +make_span_p(T *ptr, size_t count) { + return PtrOrSpan(ptr, count); +} +template +SPAN_S(T) +make_span_s(T *ptr, size_t count) { + return Span(ptr, count); +} +} // namespace + +TEST_CASE("Span codegen") { + char buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + CHECK(foo(buf) == 0 + 2 + 5); + CHECK(foo(make_span_0(buf, 8)) == 0 + 2 + 5); + CHECK(foo(make_span_p(buf, 8)) == 0 + 2 + 5); + CHECK(foo(make_span_s(buf, 8)) == 0 + 2 + 5); +} + +#endif // WITH_SPAN >= 2 + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/check/dtx_boost_pfr.cpp b/src/check/dtx_boost_pfr.cpp new file mode 100644 index 00000000..bc565f48 --- /dev/null +++ b/src/check/dtx_boost_pfr.cpp @@ -0,0 +1,82 @@ +/* dtx_.cpp -- DocTest eXtra checks + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +#if DEBUG +#ifndef WITH_BOOST_PFR +#define WITH_BOOST_PFR 1 +#endif +#endif + +#include "../conf.h" + +/************************************************************************* +// +**************************************************************************/ + +#if WITH_BOOST_PFR + +TEST_CASE("boost::pfr") { + struct Foo { + BE16 b16; + BE32 b32; + BE64 b64; + LE16 l16; + LE32 l32; + LE64 l64; + }; + + { + int i = -1; + CHECK_EQ(strcmp(pfr_str(i), "-1"), 0); + BE32 b32; + b32 = 1; + LE32 l32; + l32 = 2; + CHECK_EQ(strcmp(pfr_str(b32), "1"), 0); + CHECK_EQ(strcmp(pfr_str(l32), "2"), 0); + } + { + Foo foo; + foo.b16 = 1; + foo.b32 = 2; + foo.b64 = 3; + foo.l16 = 4; + foo.l32 = 5; + foo.l64 = 6; + CHECK_EQ(strcmp(pfr_str("foo", "=", foo), "foo = {1, 2, 3, 4, 5, 6}"), 0); + } + { +#if (ACC_ABI_BIG_ENDIAN) +#else + constexpr Foo foo{{1}, {1}, {1}, {1}, {1}, {1}}; + CHECK_EQ(strcmp(pfr_str(foo), "{256, 16777216, 72057594037927936, 1, 1, 1}"), 0); +#endif + } +} + +#endif // WITH_BOOST_PFR + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/check/dtx_rangeless.cpp b/src/check/dtx_rangeless.cpp new file mode 100644 index 00000000..af6453a8 --- /dev/null +++ b/src/check/dtx_rangeless.cpp @@ -0,0 +1,64 @@ +/* dtx_.cpp -- DocTest eXtra checks + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2023 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + */ + +#if DEBUG && 0 +#ifndef WITH_RANGELESS_FN +#define WITH_RANGELESS_FN 1 +#endif +#endif + +#if WITH_RANGELESS_FN +#define RANGELESS_FN_ENABLE_RUN_TESTS 1 +#if defined(__i386__) && defined(__MSDOS__) && defined(__DJGPP__) && defined(__GNUC__) +#define RANGELESS_FN_ENABLE_PARALLEL 0 +#elif defined(__m68k__) && defined(__atarist__) && defined(__GNUC__) +#define RANGELESS_FN_ENABLE_PARALLEL 0 +#else +// disable multithreading for now; needs CMake find_package(Threads) +#define RANGELESS_FN_ENABLE_PARALLEL 0 +#endif +#endif // WITH_RANGELESS_FN + +#include "../conf.h" + +/************************************************************************* +// +**************************************************************************/ + +#if WITH_RANGELESS_FN && RANGELESS_FN_ENABLE_RUN_TESTS + +TEST_CASE("rangeless::fn") { CHECK_NOTHROW(rangeless::fn::impl::run_tests()); } + +#if RANGELESS_FN_ENABLE_PARALLEL +TEST_CASE("rangeless::fn parallel") { + // CHECK_NOTHROW(rangeless::mt::impl::run_tests()); + ACC_UNUSED_FUNC(rangeless::mt::impl::run_tests); +} +#endif + +#endif // WITH_RANGELESS_FN + +/* vim:set ts=4 sw=4 et: */ diff --git a/src/conf.h b/src/conf.h index 1cb4970f..6e904121 100644 --- a/src/conf.h +++ b/src/conf.h @@ -83,10 +83,10 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char // don't enable before gcc-10 because of gcc bug #78010 # pragma GCC diagnostic error "-Wsuggest-override" #endif - // Some non-GLIBC toolchains do not use 'nullptr' everywhere when C++: - // openwrt-sdk-x86-64_gcc-11.2.0_musl.Linux-x86_64/staging_dir/ - // toolchain-x86_64_gcc-11.2.0_musl/include/fortify/stdlib.h: - // 51:32: error: zero as null pointer constant + // Some non-GLIBC toolchains do not use 'nullptr' everywhere when C++: + // openwrt-sdk-x86-64_gcc-11.2.0_musl.Linux-x86_64/staging_dir/ + // toolchain-x86_64_gcc-11.2.0_musl/include/fortify/stdlib.h: + // 51:32: error: zero as null pointer constant #if (ACC_CC_CLANG >= 0x050000) # pragma clang diagnostic error "-Wzero-as-null-pointer-constant" #elif (ACC_CC_GNUC >= 0x040700) && defined(__GLIBC__) @@ -115,7 +115,7 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char # include #endif -// C++ headers +// C++ system headers #include #include #include @@ -128,11 +128,21 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char #include #define upx_std_atomic(Type) std::atomic #endif -//#define DOCTEST_CONFIG_DISABLE 1 + +// C++ submodule headers #include #if WITH_BOOST_PFR -#include -#include +# include +# include +#endif +#if WITH_RANGELESS_FN +# include +#endif +#ifndef WITH_VALGRIND +#define WITH_VALGRIND 1 +#endif +#if (WITH_VALGRIND) && defined(__GNUC__) && !defined(__SANITIZE_ADDRESS__) +# include #endif // IMPORTANT: unconditionally enable assertions @@ -141,56 +151,9 @@ ACC_COMPILE_TIME_ASSERT_HEADER((char)(-1) == 255) // -funsigned-char /************************************************************************* -// +// core **************************************************************************/ -#if defined(__linux__) && !defined(__unix__) -# define __unix__ 1 -#endif - -// just in case -#undef _ -#undef __ -#undef ___ -#undef dos -#undef linux -#undef small -#undef tos -#undef unix -#if (ACC_OS_DOS32) && defined(__DJGPP__) -# undef sopen -# undef __unix__ -# undef __unix -#endif - -#define WITH_LZMA 0x443 -#define WITH_UCL 1 -#define WITH_ZLIB 1 -#if (WITH_UCL) -# define ucl_compress_config_t REAL_ucl_compress_config_t -# include -# include -# undef ucl_compress_config_t -# undef ucl_compress_config_p -#endif - -// malloc debuggers -#ifndef WITH_VALGRIND -# define WITH_VALGRIND 1 -#endif -#if (WITH_VALGRIND) && defined(__GNUC__) && !defined(__SANITIZE_ADDRESS__) -# include -#endif -#if !defined(VALGRIND_MAKE_MEM_DEFINED) -# define VALGRIND_MAKE_MEM_DEFINED(addr,len) 0 -#endif -#if !defined(VALGRIND_MAKE_MEM_NOACCESS) -# define VALGRIND_MAKE_MEM_NOACCESS(addr,len) 0 -#endif -#if !defined(VALGRIND_MAKE_MEM_UNDEFINED) -# define VALGRIND_MAKE_MEM_UNDEFINED(addr,len) 0 -#endif - // intergral types typedef acc_int8_t upx_int8_t; typedef acc_uint8_t upx_uint8_t; @@ -227,6 +190,24 @@ typedef upx_int64_t upx_off_t; // portab **************************************************************************/ +// just in case +#undef _ +#undef __ +#undef ___ +#undef dos +#undef linux +#undef small +#undef tos +#undef unix +#if defined(__linux__) && !defined(__unix__) +# define __unix__ 1 +#endif +#if (ACC_OS_DOS32) && defined(__DJGPP__) +# undef sopen +# undef __unix__ +# undef __unix +#endif + #ifndef STDIN_FILENO # define STDIN_FILENO (fileno(stdin)) #endif @@ -307,11 +288,22 @@ typedef upx_int64_t upx_off_t; /************************************************************************* -// +// util **************************************************************************/ #define CLANG_FORMAT_DUMMY_STATEMENT /*empty*/ +// malloc debuggers +#if !defined(VALGRIND_MAKE_MEM_DEFINED) +# define VALGRIND_MAKE_MEM_DEFINED(addr,len) 0 +#endif +#if !defined(VALGRIND_MAKE_MEM_NOACCESS) +# define VALGRIND_MAKE_MEM_NOACCESS(addr,len) 0 +#endif +#if !defined(VALGRIND_MAKE_MEM_UNDEFINED) +# define VALGRIND_MAKE_MEM_UNDEFINED(addr,len) 0 +#endif + #if defined(_WIN32) && defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__) # define attribute_format(a,b) __attribute__((__format__(__gnu_printf__,a,b))) #elif (ACC_CC_CLANG || ACC_CC_GNUC) @@ -488,14 +480,14 @@ constexpr bool string_ge(const char *a, const char *b) { #define UPX_F_DOS_EXE 3 #define UPX_F_DJGPP2_COFF 4 #define UPX_F_WATCOM_LE 5 -#define UPX_F_VXD_LE 6 // NOT IMPLEMENTED +//#define UPX_F_VXD_LE 6 // NOT IMPLEMENTED #define UPX_F_DOS_EXEH 7 // OBSOLETE #define UPX_F_TMT_ADAM 8 #define UPX_F_WIN32_PE 9 #define UPX_F_LINUX_i386 10 -#define UPX_F_WIN16_NE 11 // NOT IMPLEMENTED +//#define UPX_F_WIN16_NE 11 // NOT IMPLEMENTED #define UPX_F_LINUX_ELF_i386 12 -#define UPX_F_LINUX_SEP_i386 13 // NOT IMPLEMENTED +//#define UPX_F_LINUX_SEP_i386 13 // NOT IMPLEMENTED #define UPX_F_LINUX_SH_i386 14 #define UPX_F_VMLINUZ_i386 15 #define UPX_F_BVMLINUZ_i386 16 @@ -533,7 +525,7 @@ constexpr bool string_ge(const char *a, const char *b) { #define UPX_F_LINUX_ELF64_ARM 42 #define UPX_F_ATARI_TOS 129 -#define UPX_F_SOLARIS_SPARC 130 // NOT IMPLEMENTED +//#define UPX_F_SOLARIS_SPARC 130 // NOT IMPLEMENTED #define UPX_F_MACH_PPC32 131 #define UPX_F_LINUX_ELFPPC32 132 #define UPX_F_LINUX_ELF32_ARMEB 133 @@ -562,7 +554,7 @@ constexpr bool string_ge(const char *a, const char *b) { //#define M_CL1B_8 12 //#define M_CL1B_LE16 13 #define M_LZMA 14 -#define M_DEFLATE 15 /* zlib */ +#define M_DEFLATE 15 // zlib #define M_ZSTD 16 // compression methods internal usage #define M_ALL (-1) @@ -588,9 +580,20 @@ constexpr bool string_ge(const char *a, const char *b) { /************************************************************************* -// compression - callback_t +// compression - setup and callback_t **************************************************************************/ +#define WITH_LZMA 0x443 +#define WITH_UCL 1 +#define WITH_ZLIB 1 +#if (WITH_UCL) +# define ucl_compress_config_t REAL_ucl_compress_config_t +# include +# include +# undef ucl_compress_config_t +# undef ucl_compress_config_p +#endif + struct upx_callback_t; typedef upx_callback_t *upx_callback_p; typedef void (__acc_cdecl *upx_progress_func_t) diff --git a/src/util/snprintf.cpp b/src/util/snprintf.cpp index 25c2fa10..01b92621 100644 --- a/src/util/snprintf.cpp +++ b/src/util/snprintf.cpp @@ -31,6 +31,15 @@ // UPX version of string functions, with assertions and sane limits **************************************************************************/ +upx_rsize_t upx_safe_strlen(const char *s) { +#undef strlen + assert(s != nullptr); + size_t len = strlen(s); + assert(len < UPX_RSIZE_MAX_STR); + return len; +#define strlen upx_safe_strlen +} + int upx_safe_vsnprintf(char *str, upx_rsize_t max_size, const char *format, va_list ap) { #undef vsnprintf size_t size; @@ -114,13 +123,4 @@ char *upx_safe_xprintf(const char *format, ...) { return ptr; } -upx_rsize_t upx_safe_strlen(const char *s) { -#undef strlen - assert(s != nullptr); - size_t len = strlen(s); - assert(len < UPX_RSIZE_MAX_STR); - return len; -#define strlen upx_safe_strlen -} - /* vim:set ts=4 sw=4 et: */ diff --git a/src/util/snprintf.h b/src/util/snprintf.h index 19765823..a57bf366 100644 --- a/src/util/snprintf.h +++ b/src/util/snprintf.h @@ -49,6 +49,9 @@ char *upx_safe_xprintf(const char *format, ...) attribute_format(1, 2); upx_rsize_t upx_safe_strlen(const char *); // globally redirect some functions +#undef strlen +#define strlen upx_safe_strlen + #undef snprintf #undef sprintf #undef vsnprintf @@ -56,9 +59,6 @@ upx_rsize_t upx_safe_strlen(const char *); #define sprintf ERROR_sprintf_IS_DANGEROUS_USE_snprintf #define vsnprintf upx_safe_vsnprintf -#undef strlen -#define strlen upx_safe_strlen - /************************************************************************* // some unsigned char string support functions to avoid casts **************************************************************************/ diff --git a/src/util/xspan.cpp b/src/util/xspan.cpp index 420be59b..ce373055 100644 --- a/src/util/xspan.cpp +++ b/src/util/xspan.cpp @@ -69,727 +69,4 @@ SPAN_NAMESPACE_END #endif // WITH_SPAN -// lots of tests (and probably quite a number of redundant tests) - -/************************************************************************* -// -**************************************************************************/ - -TEST_CASE("basic xspan usage") { - char buf[4] = {0, 1, 2, 3}; - - SUBCASE("SPAN_x") { - SPAN_0(char) a0 = nullptr; - - SPAN_0(char) b0 = buf; - SPAN_P(char) bp = buf; - - SPAN_0(char) c0 = SPAN_0_MAKE(char, buf); - SPAN_P(char) cp = SPAN_P_MAKE(char, buf); - SPAN_S(char) cs = SPAN_S_MAKE(char, buf, sizeof(buf)); - - SPAN_0(const char) const x0 = SPAN_0_MAKE(const char, buf); - SPAN_P(const char) const xp = SPAN_P_MAKE(const char, buf); - SPAN_S(const char) const xs = SPAN_S_MAKE(const char, buf, sizeof(buf)); - SPAN_P(const char) const yp = xs; - SPAN_0(const char) const z0p = yp; - SPAN_0(const char) const z0s = xs; - - CHECK((a0 == nullptr)); - CHECK(c0 == b0); - CHECK(cp == bp); - CHECK(cs == bp); - CHECK(x0 == z0p); - CHECK(xp == z0s); - - CHECK(raw_bytes(c0, 4) == buf); - CHECK(raw_index_bytes(c0, 1, 3) == buf + 1); - CHECK(raw_bytes(cp, 4) == buf); - CHECK(raw_index_bytes(cp, 1, 3) == buf + 1); - CHECK(raw_bytes(cs, 4) == buf); - CHECK(raw_index_bytes(cs, 1, 3) == buf + 1); - CHECK_THROWS(raw_bytes(cs, 5)); - CHECK_THROWS(raw_index_bytes(cs, 1, 4)); - } - - SUBCASE("SPAN_x_VAR") { - SPAN_0_VAR(char, a0, nullptr); - - SPAN_0_VAR(char, b0, buf); - SPAN_P_VAR(char, bp, buf); - - SPAN_0_VAR(char, c0, buf, sizeof(buf)); - SPAN_P_VAR(char, cp, buf, sizeof(buf)); - SPAN_S_VAR(char, cs, buf, sizeof(buf)); - - SPAN_0_VAR(char, d0, buf + 1, sizeof(buf), buf); - SPAN_P_VAR(char, dp, buf + 1, sizeof(buf), buf); - SPAN_S_VAR(char, ds, buf + 1, sizeof(buf), buf); - - SPAN_0_VAR(const char, const x0, buf, sizeof(buf)); - SPAN_P_VAR(const char, const xp, buf, sizeof(buf)); - SPAN_S_VAR(const char, const xs, buf, sizeof(buf)); - SPAN_P_VAR(const char, const yp, xs); - SPAN_0_VAR(const char, const z0p, yp); - SPAN_0_VAR(const char, const z0s, xs); - - CHECK((a0 == nullptr)); - CHECK(c0 == b0); - CHECK(cp == bp); - CHECK(cs == bp); - CHECK(d0 == dp); - CHECK(d0 == ds); - CHECK(x0 == z0p); - CHECK(xp == z0s); - - CHECK(raw_bytes(c0, 4) == buf); - CHECK(raw_index_bytes(c0, 1, 3) == buf + 1); - CHECK(raw_bytes(cp, 4) == buf); - CHECK(raw_index_bytes(cp, 1, 3) == buf + 1); - CHECK(raw_bytes(cs, 4) == buf); - CHECK(raw_index_bytes(cs, 1, 3) == buf + 1); - CHECK_THROWS(raw_bytes(cs, 5)); - CHECK_THROWS(raw_index_bytes(cs, 1, 4)); - } - - SUBCASE("xspan in class") { - struct MyType { - SPAN_0(char) s0; - SPAN_P(char) sp; - SPAN_S(char) ss; -#if __cplusplus >= 201103L - SPAN_0(char) x0 = nullptr; -#endif -#if WITH_SPAN >= 2 - // much nicer syntax when using fully checked xspan: - MyType(char *b, size_t n, bool) : s0(b, n), sp(b, n), ss(b, n) {} -#endif - MyType(char *b, size_t n) - : s0(SPAN_0_MAKE(char, b, n)), sp(SPAN_P_MAKE(char, b, n)), - ss(SPAN_S_MAKE(char, b, n)) { - UNUSED(n); - } - }; - MyType x(buf, sizeof(buf)); - MyType y = MyType(buf, sizeof(buf)); - CHECK(x.s0 == y.sp); - } -} - -/************************************************************************* -// -**************************************************************************/ - -#if WITH_SPAN >= 2 - -TEST_CASE("PtrOrSpanOrNull") { - char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; - char *base_buf = real_buf + 2; - char *const my_null = nullptr; - typedef PtrOrSpanOrNull Span0; - - // basic nullptr - CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = my_null); - CHECK_NOTHROW(Span0(base_buf, 4, base_buf).assign(my_null)); - // basic range checking - CHECK_NOTHROW(Span0(base_buf, 4, base_buf)); - CHECK_NOTHROW(Span0(base_buf, 0, base_buf)); - CHECK_NOTHROW(Span0(base_buf, 0, base_buf) - 0); - CHECK_THROWS(Span0(base_buf, 0, base_buf) + 1); - CHECK_THROWS(Span0(base_buf, 0, base_buf) - 1); - CHECK_NOTHROW(Span0(base_buf, 4, base_buf) + 4); - CHECK_THROWS(Span0(base_buf, 4, base_buf) + 5); - CHECK_THROWS(Span0(base_buf - 1, 4, base_buf)); - CHECK_THROWS(Span0(base_buf + 1, 0, base_buf)); - // basic same base - CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 3, base_buf)); - CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 1, base_buf)); - CHECK_NOTHROW(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 5, base_buf)); - CHECK_THROWS(Span0(base_buf, 4, base_buf) = Span0(base_buf + 1, 3, base_buf + 1)); - - Span0 a1(nullptr); - assert(a1 == nullptr); - assert(a1.raw_ptr() == nullptr); - assert(a1.raw_base() == nullptr); - assert(a1.raw_size_in_bytes() == 0u); - CHECK_THROWS(*a1); - CHECK_THROWS(a1[0]); - - Span0 a2 = nullptr; - assert(a2 == nullptr); - assert(a2.raw_ptr() == nullptr); - assert(a2.raw_base() == nullptr); - assert(a2.raw_size_in_bytes() == 0u); - CHECK_THROWS(*a2); - CHECK_THROWS(a2[0]); - - Span0 base0(nullptr, 4, base_buf); - assert(base0.raw_ptr() == nullptr); - assert(base0.raw_base() == base_buf); - assert(base0.raw_size_in_bytes() == 4u); - CHECK_THROWS(*base0); // nullptr - CHECK_THROWS(base0[0]); // nullptr - CHECK_THROWS(base0 + 1); // nullptr - - Span0 base4(base_buf, 4); - assert(base4.raw_ptr() == base_buf); - assert(base4.raw_base() == base_buf); - assert(base4.raw_size_in_bytes() == 4u); - - a1 = base_buf; - a1 = base0; - assert(a1 == nullptr); - assert(a1.raw_ptr() == nullptr); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - a1 = base4; - assert(a1 == base_buf); - assert(a1.raw_ptr() == base_buf); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - - a1 = base_buf; - assert(a1 != nullptr); - a1 = base_buf + 1; - CHECK(*a1++ == 1); - CHECK(*++a1 == 3); - CHECK(*a1 == 3); - a1 = base_buf + 4; // at the end of buffer - CHECK_THROWS(*a1); - CHECK_THROWS(a1 = base_buf + 5); // range error - assert(a1 == base_buf + 4); - CHECK(a1[-4] == 0); - CHECK_THROWS(a1[-5]); // range error - a1 = base_buf; - CHECK(*a1 == 0); - - Span0 new_base4(base_buf + 2, 4); - CHECK_THROWS(a1 = new_base4); // not same base - a2 = new_base4; - CHECK_THROWS(a2 = base4); // not same base - - Span0 s0_no_base(nullptr); - Span0 s0_with_base(nullptr, 4, base_buf); - s0_no_base = nullptr; - s0_with_base = nullptr; - s0_with_base = s0_no_base; - assert(s0_no_base.raw_base() == nullptr); - assert(s0_with_base.raw_base() == base_buf); - s0_no_base = s0_with_base; - assert(s0_no_base.raw_base() == base_buf); - assert(s0_no_base.raw_ptr() == nullptr); - assert(s0_with_base.raw_ptr() == nullptr); - s0_no_base = my_null; - s0_with_base = my_null; -} - -/************************************************************************* -// -**************************************************************************/ - -TEST_CASE("PtrOrSpan") { - char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; - char *base_buf = real_buf + 2; - char *const my_null = nullptr; - typedef PtrOrSpan SpanP; - - // basic nullptr - CHECK_THROWS(SpanP(base_buf, 4, base_buf) = my_null); - CHECK_THROWS(SpanP(base_buf, 4, base_buf).assign(my_null)); - // basic range checking - CHECK_NOTHROW(SpanP(base_buf, 4, base_buf)); - CHECK_NOTHROW(SpanP(base_buf, 0, base_buf)); - CHECK_NOTHROW(SpanP(base_buf, 0, base_buf) - 0); - CHECK_THROWS(SpanP(base_buf, 0, base_buf) + 1); - CHECK_THROWS(SpanP(base_buf, 0, base_buf) - 1); - CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) + 4); - CHECK_THROWS(SpanP(base_buf, 4, base_buf) + 5); - CHECK_THROWS(SpanP(base_buf - 1, 4, base_buf)); - CHECK_THROWS(SpanP(base_buf + 1, 0, base_buf)); - // basic same base - CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 3, base_buf)); - CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 1, base_buf)); - CHECK_NOTHROW(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 5, base_buf)); - CHECK_THROWS(SpanP(base_buf, 4, base_buf) = SpanP(base_buf + 1, 3, base_buf + 1)); - - SpanP x1(base_buf, 0); - assert(x1 != nullptr); - assert(x1.raw_ptr() == base_buf); - assert(x1.raw_base() == base_buf); - assert(x1.raw_size_in_bytes() == 0u); - CHECK_THROWS(*x1); - CHECK_THROWS(x1[0]); - - SpanP a2 = base_buf; - assert(a2 != nullptr); - assert(a2.raw_ptr() == base_buf); - assert(a2.raw_base() == nullptr); - assert(a2.raw_size_in_bytes() == 0u); - CHECK(*a2 == 0); - CHECK(a2[1] == 1); - - SpanP base0(base_buf, 4, base_buf); - assert(base0.raw_ptr() == base_buf); - assert(base0.raw_base() == base_buf); - assert(base0.raw_size_in_bytes() == 4u); - - SpanP base4(base_buf, 4); - assert(base4.raw_ptr() == base_buf); - assert(base4.raw_base() == base_buf); - assert(base4.raw_size_in_bytes() == 4u); - - SpanP a1(base_buf, 4); - a1 = base_buf; - a1 = base0; - assert(a1 == base0); - assert(a1 != nullptr); - assert(a1.raw_ptr() == base0.raw_ptr()); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - a1 = base4; - assert(a1 == base_buf); - assert(a1.raw_ptr() == base_buf); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - - a1 = base_buf; - a1 = base_buf + 1; - CHECK(*a1++ == 1); - CHECK(*++a1 == 3); - CHECK(*a1 == 3); - a1 = base_buf + 4; // at the end of buffer - CHECK_THROWS(*a1); - CHECK_THROWS(a1 = base_buf + 5); // range error - assert(a1 == base_buf + 4); - CHECK(a1[-4] == 0); - CHECK_THROWS(a1[-5]); // range error - a1 = base_buf; - CHECK(*a1 == 0); - - SpanP new_base4(base_buf + 2, 4); - CHECK_THROWS(a1 = new_base4); // not same base - a2 = new_base4; - CHECK_THROWS(a2 = base4); // not same base - - SpanP sp_no_base(base_buf); - SpanP sp_with_base(base_buf, 4, base_buf); - assert(sp_no_base.raw_base() == nullptr); - assert(sp_with_base.raw_base() == base_buf); - CHECK_THROWS(sp_no_base = my_null); // nullptr assignment - CHECK_THROWS(sp_with_base = my_null); // nullptr assignment -#if SPAN_CONFIG_ENABLE_SPAN_CONVERSION - typedef PtrOrSpanOrNull Span0; - Span0 s0_no_base(nullptr); - Span0 s0_with_base(nullptr, 4, base_buf); - CHECK_THROWS(sp_no_base = s0_no_base); // nullptr assignment - CHECK_THROWS(sp_no_base = s0_with_base); // nullptr assignment - CHECK_THROWS(sp_with_base = s0_no_base); // nullptr assignment - CHECK_THROWS(sp_with_base = s0_with_base); // nullptr assignment -#endif -} - -/************************************************************************* -// -**************************************************************************/ - -TEST_CASE("Span") { - char real_buf[2 + 6 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 124, 125}; - char *base_buf = real_buf + 2; - char *const my_null = nullptr; - typedef Span SpanS; - - // basic nullptr - CHECK_THROWS(SpanS(base_buf, 4, base_buf) = my_null); - CHECK_THROWS(SpanS(base_buf, 4, base_buf).assign(my_null)); - // basic range checking - CHECK_NOTHROW(SpanS(base_buf, 4, base_buf)); - CHECK_NOTHROW(SpanS(base_buf, 0, base_buf)); - CHECK_NOTHROW(SpanS(base_buf, 0, base_buf) - 0); - CHECK_THROWS(SpanS(base_buf, 0, base_buf) + 1); - CHECK_THROWS(SpanS(base_buf, 0, base_buf) - 1); - CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) + 4); - CHECK_THROWS(SpanS(base_buf, 4, base_buf) + 5); - CHECK_THROWS(SpanS(base_buf - 1, 4, base_buf)); - CHECK_THROWS(SpanS(base_buf + 1, 0, base_buf)); - // basic same base - CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 3, base_buf)); - CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 1, base_buf)); - CHECK_NOTHROW(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 5, base_buf)); - CHECK_THROWS(SpanS(base_buf, 4, base_buf) = SpanS(base_buf + 1, 3, base_buf + 1)); - - SpanS x1(base_buf, 0); - assert(x1 != nullptr); - assert(x1.raw_ptr() == base_buf); - assert(x1.raw_base() == base_buf); - assert(x1.raw_size_in_bytes() == 0u); - CHECK_THROWS(*x1); - CHECK_THROWS(x1[0]); - - SpanS a2(base_buf, 4); - assert(a2 != nullptr); - assert(a2.raw_ptr() == base_buf); - assert(a2.raw_base() == base_buf); - assert(a2.raw_size_in_bytes() == 4u); - CHECK(*a2 == 0); - CHECK(a2[1] == 1); - - SpanS base0(base_buf, 4, base_buf); - assert(base0.raw_ptr() == base_buf); - assert(base0.raw_base() == base_buf); - assert(base0.raw_size_in_bytes() == 4u); - - SpanS base4(base_buf, 4); - assert(base4.raw_ptr() == base_buf); - assert(base4.raw_base() == base_buf); - assert(base4.raw_size_in_bytes() == 4u); - - SpanS a1(base_buf, 4); - a1 = base_buf; - a1 = base0; - assert(a1 == base0); - assert(a1 != nullptr); - assert(a1.raw_ptr() == base0.raw_ptr()); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - a1 = base4; - assert(a1 == base_buf); - assert(a1.raw_ptr() == base_buf); - assert(a1.raw_base() == base_buf); - assert(a1.raw_size_in_bytes() == 4u); - - a1 = base_buf; - a1 = base_buf + 1; - CHECK(*a1++ == 1); - CHECK(*++a1 == 3); - CHECK(*a1 == 3); - a1 = base_buf + 4; // at the end of buffer - CHECK_THROWS(*a1); - CHECK_THROWS(a1 = base_buf + 5); // range error - assert(a1 == base_buf + 4); - CHECK(a1[-4] == 0); - CHECK_THROWS(a1[-5]); // range error - a1 = base_buf; - CHECK(*a1 == 0); - - SpanS new_base4(base_buf + 2, 4); - CHECK_THROWS(a1 = new_base4); // not same base - CHECK_THROWS(a2 = new_base4); // not same base - - SpanS ss_with_base(base_buf, 4, base_buf); - assert(ss_with_base.raw_base() == base_buf); - CHECK_THROWS(ss_with_base = my_null); // nullptr assignment -#if SPAN_CONFIG_ENABLE_SPAN_CONVERSION - { - typedef PtrOrSpanOrNull Span0; - // v0 nullptr, b0 base, b1 base + 1 - const Span0 v0_v0(nullptr); - const Span0 v0_b0(nullptr, 4, base_buf); - const Span0 v0_b1(nullptr, 3, base_buf + 1); - const Span0 b0_v0(base_buf); - const Span0 b0_b0(base_buf, 4, base_buf); - CHECK_THROWS(SPAN_0_MAKE(char, base_buf, 3, base_buf + 1)); // b0_b1 - const Span0 b1_v0(base_buf + 1); - const Span0 b1_b0(base_buf + 1, 4, base_buf); - const Span0 b1_b1(base_buf + 1, 3, base_buf + 1); - CHECK_THROWS(ss_with_base = v0_v0); // nullptr assignment - CHECK_THROWS(ss_with_base = v0_b0); // nullptr assignment - CHECK_THROWS(ss_with_base = v0_b1); // nullptr assignment - CHECK_NOTHROW(ss_with_base = b0_v0); - CHECK_NOTHROW(ss_with_base = b0_b0); - CHECK_NOTHROW(ss_with_base = b1_v0); - CHECK_NOTHROW(ss_with_base = b1_b0); - CHECK_THROWS(ss_with_base = b1_b1); // different base - CHECK_THROWS(SPAN_S_MAKE(char, v0_v0)); - CHECK_THROWS(SPAN_S_MAKE(char, v0_b0)); - CHECK_THROWS(SPAN_S_MAKE(char, v0_b1)); - CHECK_THROWS(SPAN_S_MAKE(char, b0_v0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b0_b0)); - CHECK_THROWS(SPAN_S_MAKE(char, b1_v0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b1)); - // - CHECK((SPAN_S_MAKE(char, b0_b0).raw_base() == base_buf)); - CHECK((SPAN_S_MAKE(char, b1_b0).raw_base() == base_buf)); - CHECK((SPAN_S_MAKE(char, b1_b1).raw_base() == base_buf + 1)); - } - { - typedef PtrOrSpan SpanP; - // v0 nullptr, b0 base, b1 base + 1 - const SpanP b0_v0(base_buf); - const SpanP b0_b0(base_buf, 4, base_buf); - CHECK_THROWS(SPAN_P_MAKE(char, base_buf, 3, base_buf + 1)); // b0_b1 - const SpanP b1_v0(base_buf + 1); - const SpanP b1_b0(base_buf + 1, 4, base_buf); - const SpanP b1_b1(base_buf + 1, 3, base_buf + 1); - CHECK_NOTHROW(ss_with_base = b0_v0); - CHECK_NOTHROW(ss_with_base = b0_b0); - CHECK_NOTHROW(ss_with_base = b1_v0); - CHECK_NOTHROW(ss_with_base = b1_b0); - CHECK_THROWS(ss_with_base = b1_b1); // different base - CHECK_THROWS(SPAN_S_MAKE(char, b0_v0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b0_b0)); - CHECK_THROWS(SPAN_S_MAKE(char, b1_v0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b0)); - CHECK_NOTHROW(SPAN_S_MAKE(char, b1_b1)); - // - CHECK((SPAN_S_MAKE(char, b0_b0).raw_base() == base_buf)); - CHECK((SPAN_S_MAKE(char, b1_b0).raw_base() == base_buf)); - CHECK((SPAN_S_MAKE(char, b1_b1).raw_base() == base_buf + 1)); - } -#endif -} - -/************************************************************************* -// -**************************************************************************/ - -TEST_CASE("Span void ptr") { - static char a[4] = {0, 1, 2, 3}; - SPAN_0(void) a0(a, 4); - SPAN_P(void) ap(a, 4); - SPAN_S(void) as(a, 4); - SPAN_0(const void) c0(a, 4); - SPAN_P(const void) cp(a, 4); - SPAN_S(const void) cs(a, 4); - static const char b[4] = {0, 1, 2, 3}; - SPAN_0(const void) b0(b, 4); - SPAN_P(const void) bp(b, 4); - SPAN_S(const void) bs(b, 4); -} - -TEST_CASE("Span deref/array/arrow") { - static char real_a[2 + 4 + 2] = {126, 127, 0, 1, 2, 3, 124, 125}; - static char *a = real_a + 2; - SPAN_0(char) a0(a, 4); - SPAN_P(char) ap(a, 4); - SPAN_S(char) as(a, 4); - CHECK_THROWS(a0[4]); - CHECK_THROWS(a0[-1]); - CHECK_THROWS(a0[-2]); - a0 += 2; - CHECK(*a0 == 2); - CHECK(a0[-1] == 1); - CHECK(a0[0] == 2); - CHECK(a0[1] == 3); - ap += 2; - CHECK(*ap == 2); - CHECK(ap[-1] == 1); - CHECK(ap[0] == 2); - CHECK(ap[1] == 3); - as += 2; - CHECK(*as == 2); - CHECK(as[-1] == 1); - CHECK(as[0] == 2); - CHECK(as[1] == 3); -} - -TEST_CASE("Span subspan") { - static char buf[4] = {0, 1, 2, 3}; - SPAN_S(char) as(buf, 4); - CHECK(as.subspan(1, 1)[0] == 1); - CHECK((as + 1).subspan(1, 1)[0] == 2); - CHECK((as + 2).subspan(0, -2)[0] == 0); - CHECK_THROWS(as.subspan(1, 0)[0]); - CHECK_THROWS(as.subspan(1, 1)[-1]); -} - -TEST_CASE("Span constness") { - static char buf[4] = {0, 1, 2, 3}; - - SPAN_0(char) b0(buf, 4); - SPAN_P(char) bp(buf, 4); - SPAN_S(char) bs(buf, 4); - - SPAN_0(char) s0(b0); - SPAN_P(char) sp(bp); - SPAN_S(char) ss(bs); - - SPAN_0(const char) b0c(buf, 4); - SPAN_P(const char) bpc(buf, 4); - SPAN_S(const char) bsc(buf, 4); - - SPAN_0(const char) s0c(b0c); - SPAN_P(const char) spc(bpc); - SPAN_S(const char) ssc(bsc); - - SPAN_0(const char) x0c(b0); - SPAN_P(const char) xpc(bp); - SPAN_S(const char) xsc(bs); - - CHECK(ptr_diff_bytes(b0, buf) == 0); - CHECK(ptr_diff_bytes(bp, buf) == 0); - CHECK(ptr_diff_bytes(bs, buf) == 0); - CHECK(ptr_diff_bytes(s0, buf) == 0); - CHECK(ptr_diff_bytes(sp, buf) == 0); - CHECK(ptr_diff_bytes(bs, buf) == 0); - // - CHECK(ptr_diff_bytes(s0, bp) == 0); - CHECK(ptr_diff_bytes(s0, sp) == 0); - CHECK(ptr_diff_bytes(s0, ss) == 0); - // - CHECK(ptr_diff_bytes(s0c, b0c) == 0); - CHECK(ptr_diff_bytes(spc, bpc) == 0); - CHECK(ptr_diff_bytes(ssc, bsc) == 0); -} - -/************************************************************************* -// -**************************************************************************/ - -#if !defined(DOCTEST_CONFIG_DISABLE) -namespace { -int my_memcmp_v1(SPAN_P(const void) a, SPAN_0(const void) b, size_t n) { - if (b == nullptr) - return -2; - SPAN_0(const void) x(a); - return memcmp(x, b, n); -} -int my_memcmp_v2(SPAN_P(const char) a, SPAN_0(const char) b, size_t n) { - if (a == b) - return 0; - if (b == nullptr) - return -2; - a += 1; - b -= 1; - SPAN_0(const char) x(a); - SPAN_0(const char) y = b; - return memcmp(x, y, n); -} -} // namespace -#endif - -TEST_CASE("PtrOrSpan") { - static const char buf[4] = {0, 1, 2, 3}; - CHECK(my_memcmp_v1(buf, nullptr, 4) == -2); - CHECK(my_memcmp_v2(buf + 4, buf + 4, 999) == 0); - CHECK(my_memcmp_v2(buf, buf + 2, 3) == 0); -} - -/************************************************************************* -// -**************************************************************************/ - -TEST_CASE("PtrOrSpan char") { - char real_buf[2 + 8 + 2] = {126, 127, 0, 1, 2, 3, 4, 5, 6, 7, 124, 125}; - char *buf = real_buf + 2; - SPAN_P(char) a(buf, SpanSizeInBytes(8)); - SPAN_P(char) b = a.subspan(0, 7); - SPAN_P(char) c = (b + 1).subspan(0, 6); - a += 1; - CHECK(*a == 1); - *a++ += 1; - *b++ = 1; - CHECK(a == buf + 2); - CHECK(b == buf + 1); - CHECK(c == buf + 1); - CHECK(*b == 2); - CHECK(*c == 2); - CHECK(a.raw_size_in_bytes() == 8u); - CHECK(b.raw_size_in_bytes() == 7u); - CHECK(c.raw_size_in_bytes() == 6u); - CHECK(a.raw_base() == buf); - CHECK(b.raw_base() == buf); - CHECK(c.raw_base() == buf + 1); -#ifdef UPX_VERSION_HEX - CHECK(get_le32(a) != 0); -#endif - ++c; - c++; -#ifdef UPX_VERSION_HEX - CHECK(get_le32(c) != 0); -#endif - ++c; -#ifdef UPX_VERSION_HEX - CHECK_THROWS(get_le32(c)); -#endif - ++b; - b++; - b += 4; - CHECK(b.raw_ptr() == buf + 7); - CHECK_THROWS(*b); - CHECK(a.raw_size_in_bytes() == 8u); - a = b; - CHECK(a.raw_size_in_bytes() == 8u); - CHECK(a.raw_ptr() == buf + 7); - a++; - CHECK_THROWS(*a); - CHECK_THROWS(raw_bytes(a, 1)); - a = b; - CHECK_THROWS(a = c); - *a = 0; - a = buf; -#ifdef UPX_VERSION_HEX - CHECK(upx_safe_strlen(a) == 7u); -#endif -} - -TEST_CASE("PtrOrSpan int") { - int buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; - SPAN_P(int) a(buf, SpanCount(8)); - CHECK(a.raw_size_in_bytes() == 8 * sizeof(int)); - SPAN_P(int) b = a.subspan(0, 7); - CHECK(b.raw_size_in_bytes() == 7 * sizeof(int)); - SPAN_P(int) c = (b + 1).subspan(0, 6); - CHECK(c.raw_size_in_bytes() == 6 * sizeof(int)); - a += 1; - CHECK(*a == 1); - CHECK(*a++ == 1); - CHECK(*++a == 3); - CHECK(--*a == 2); - CHECK(*a-- == 2); - CHECK(*b == 0); - CHECK(*c == 1); - a = buf + 7; -#ifdef UPX_VERSION_HEX - CHECK(get_le32(a) == ne32_to_le32(7)); -#endif - a++; -#ifdef UPX_VERSION_HEX - CHECK_THROWS(get_le32(a)); -#endif - CHECK_THROWS(raw_bytes(a, 1)); -} - -/************************************************************************* -// codegen -**************************************************************************/ - -namespace { -template -__acc_static_noinline int foo(T p) { - unsigned r = 0; - r += *p++; - r += *++p; - p += 3; - r += *p; - return r; -} - -template -SPAN_0(T) -make_span_0(T *ptr, size_t count) { - return PtrOrSpanOrNull(ptr, count); -} -template -SPAN_P(T) -make_span_p(T *ptr, size_t count) { - return PtrOrSpan(ptr, count); -} -template -SPAN_S(T) -make_span_s(T *ptr, size_t count) { - return Span(ptr, count); -} -} // namespace - -TEST_CASE("Span codegen") { - char buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; - CHECK(foo(buf) == 0 + 2 + 5); - CHECK(foo(make_span_0(buf, 8)) == 0 + 2 + 5); - CHECK(foo(make_span_p(buf, 8)) == 0 + 2 + 5); - CHECK(foo(make_span_s(buf, 8)) == 0 + 2 + 5); -} - -#endif // WITH_SPAN >= 2 - /* vim:set ts=4 sw=4 et: */ diff --git a/src/util/xspan_impl_common.h b/src/util/xspan_impl_common.h index 91dc1232..503e678f 100644 --- a/src/util/xspan_impl_common.h +++ b/src/util/xspan_impl_common.h @@ -52,7 +52,7 @@ pointer base; size_type size_in_bytes; // debug - internal sanity check; also serves as pseudo-documentation -#if DEBUG || 1 +#if DEBUG __acc_noinline void assertInvariants() const { if __acc_cte (configRequirePtr) assert(ptr != nullptr);