From 68a21c5a5952913d6192be747efd245887bc36cc Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:24:54 +0200 Subject: [PATCH 1/4] Fix substring calculation in CSV cell parsing and add lexical_cast for signed and unsigned char --- include/xtensor/io/xcsv.hpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/xtensor/io/xcsv.hpp b/include/xtensor/io/xcsv.hpp index 080ccaf54..2fd347745 100644 --- a/include/xtensor/io/xcsv.hpp +++ b/include/xtensor/io/xcsv.hpp @@ -66,7 +66,7 @@ namespace xt } size_t last = cell.find_last_not_of(' '); - return cell.substr(first, last == std::string::npos ? cell.size() : last + 1); + return cell.substr(first, last == std::string::npos ? cell.size() : last - first + 1); } template <> @@ -93,6 +93,18 @@ namespace xt return std::stoi(cell); } + template <> + inline signed char lexical_cast(const std::string& cell) + { + return static_cast(std::stoi(cell)); + } + + template <> + inline unsigned char lexical_cast(const std::string& cell) + { + return static_cast(std::stoul(cell)); + } + template <> inline long lexical_cast(const std::string& cell) { From 005b0c6d1dd1f897ae47b1393b1c07f327b066e6 Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 6 May 2026 16:05:50 +0200 Subject: [PATCH 2/4] Add ASAN CI build --- .github/workflows/sanitizers.yml | 98 ++++++++++++++++++++++++++++++++ cmake/sanitizers.cmake | 44 ++++++++++++++ test/CMakeLists.txt | 3 + 3 files changed, 145 insertions(+) create mode 100644 .github/workflows/sanitizers.yml create mode 100644 cmake/sanitizers.cmake diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml new file mode 100644 index 000000000..4ec16358b --- /dev/null +++ b/.github/workflows/sanitizers.yml @@ -0,0 +1,98 @@ +name: Sanitizers +on: + workflow_dispatch: + pull_request: + push: + branches: [master] +concurrency: + group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }} + cancel-in-progress: true +defaults: + run: + shell: bash -e -l {0} +jobs: + build: + runs-on: ${{ matrix.os }} + name: sanitizer / ${{ matrix.sys.compiler }} ${{ matrix.sys.version }} / ${{ matrix.config.name }} / ${{ matrix.sys.name }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04] + sys: + - {compiler: clang, version: '19', name: asan} + - {compiler: clang, version: '21', name: asan} + config: + - {name: Debug} + + steps: + + - name: Install LLVM and Clang + if: matrix.sys.compiler == 'clang' + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{matrix.sys.version}} + sudo apt-get install -y clang-tools-${{matrix.sys.version}} + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${{matrix.sys.version}} 200 + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{matrix.sys.version}} 200 + sudo update-alternatives --install /usr/bin/clang-scan-deps clang-scan-deps /usr/bin/clang-scan-deps-${{matrix.sys.version}} 200 + sudo update-alternatives --set clang /usr/bin/clang-${{matrix.sys.version}} + sudo update-alternatives --set clang++ /usr/bin/clang++-${{matrix.sys.version}} + sudo update-alternatives --set clang-scan-deps /usr/bin/clang-scan-deps-${{matrix.sys.version}} + + - name: Checkout code + uses: actions/checkout@v6 + + - name: Set conda environment + uses: mamba-org/setup-micromamba@main + with: + environment-name: myenv + environment-file: environment-dev.yml + init-shell: bash + cache-downloads: true + + - name: Configure using CMake + run: | + export CC=clang + export CXX=clang++ + cmake -G Ninja \ + -Bbuild \ + -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} \ + -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ + -DBUILD_TESTS=ON \ + -DUSE_SANITIZER=address + + - name: Build tests + working-directory: build + run: cmake --build . --config ${{matrix.config.name}} --target test_xtensor_lib --parallel 8 + + - name: Run tests + working-directory: build + run: | + export ASAN_OPTIONS=log_path=asan_log_:alloc_dealloc_mismatch=0:halt_on_error=0:handle_abort=0 + export ASAN_SAVE_DUMPS=AsanDump.dmp + ctest -R ^xtest$ --output-on-failure + + - name: Upload ASAN log + if: always() + uses: actions/upload-artifact@v6 + with: + name: asan-log-${{ matrix.sys.compiler }}-${{ matrix.sys.version }}-${{ matrix.config.name }}-${{ runner.os }} + path: '**/asan_log_*' + if-no-files-found: ignore + + - name: Upload ASAN dump + if: always() + uses: actions/upload-artifact@v6 + with: + name: asan-dump-${{ matrix.sys.compiler }}-${{ matrix.sys.version }}-${{ matrix.config.name }}-${{ runner.os }} + path: '**/AsanDump.dmp' + if-no-files-found: ignore + + - name: Return errors if ASAN log content is not empty + if: always() + run: | + if [ -n "$(find build/test -name 'asan_log_*' -type f -size +0 2>/dev/null)" ]; then + echo "ASAN detected errors. See the log for details." + exit 1 + fi diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake new file mode 100644 index 000000000..d4b3c54f9 --- /dev/null +++ b/cmake/sanitizers.cmake @@ -0,0 +1,44 @@ +set(AVAILABLE_SANITIZERS "address;leak;memory;thread;undefined") +OPTION(USE_SANITIZER "Enable sanitizer(s). Options are: ${AVAILABLE_SANITIZERS}. Case insensitive; multiple options delimited by comma or space possible." "") +string(TOLOWER "${USE_SANITIZER}" USE_SANITIZER) + +if(USE_SANITIZER AND NOT (CMAKE_BUILD_TYPE IN_LIST "Debug;RelWithDebInfo")) + message(FATAL_ERROR "❌ Sanitizer only supported in Debug and RelWithDebInfo build types. Current: ${CMAKE_BUILD_TYPE}") +endif() + +if(USE_SANITIZER) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") + + if(USE_SANITIZER MATCHES "address") + string(APPEND CMAKE_CXX_FLAGS " /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION") + string(APPEND CMAKE_EXE_LINKER_FLAGS " /fsanitize=address") + else() + message(FATAL_ERROR "❌ Sanitizer not supported by MSVC: ${USE_SANITIZER}. It only supports 'address'.") + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + if(USE_SANITIZER MATCHES "address") + string(APPEND CMAKE_CXX_FLAGS " /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION") + string(APPEND CMAKE_EXE_LINKER_FLAGS " /fsanitize=address") + else() + message(FATAL_ERROR "❌ Sanitizer not supported by Clang-MSVC: ${USE_SANITIZER}. It only supports 'address'.") + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + foreach(sanitizer ${USE_SANITIZER}) + if(NOT ${sanitizer} IN_LIST AVAILABLE_SANITIZERS) + message(FATAL_ERROR "❌ Sanitizer not supported: ${sanitizer}. It should be one of: ${AVAILABLE_SANITIZERS}.") + endif() + string(APPEND CMAKE_CXX_FLAGS " -fsanitize=${sanitizer}") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=${sanitizer}") + if(${sanitizer} MATCHES "memory") + string(APPEND CMAKE_CXX_FLAGS " -fsanitize-memory-track-origins -fPIE -pie") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize-memory-track-origins -fPIE -pie") + endif() + endforeach() + string(APPEND CMAKE_CXX_FLAGS " -fno-omit-frame-pointer") + else() + message(FATAL_ERROR "❌ Sanitizer: Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") + endif() + + message(STATUS "🔍 Using sanitizer: ${USE_SANITIZER}") +endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8341230ee..deb7ddf22 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -231,6 +231,9 @@ endforeach() file(GLOB XTENSOR_PREPROCESS_FILES files/cppy_source/*.cppy) +# Sanitizer support +include(${CMAKE_SOURCE_DIR}/cmake/sanitizers.cmake) + # This target should only be run when the test source files have been changed. add_custom_target( preprocess_cppy From a8bc5e8ebfef43a0ce72c82e79edf7ae4d63355f Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 6 May 2026 16:27:57 +0200 Subject: [PATCH 3/4] try fix --- .github/workflows/sanitizers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 4ec16358b..da585bf2d 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -57,7 +57,7 @@ jobs: export CXX=clang++ cmake -G Ninja \ -Bbuild \ - -DCMAKE_BUILD_TYPE:STRING=${{matrix.config.name}} \ + -DCMAKE_BUILD_TYPE=${{matrix.config.name}} \ -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ -DBUILD_TESTS=ON \ -DUSE_SANITIZER=address From fdb27adf8c53c99ba99c85ff80db1d1cff8162de Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 6 May 2026 20:38:05 +0200 Subject: [PATCH 4/4] fix --- cmake/sanitizers.cmake | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake index d4b3c54f9..716021f96 100644 --- a/cmake/sanitizers.cmake +++ b/cmake/sanitizers.cmake @@ -1,9 +1,9 @@ -set(AVAILABLE_SANITIZERS "address;leak;memory;thread;undefined") -OPTION(USE_SANITIZER "Enable sanitizer(s). Options are: ${AVAILABLE_SANITIZERS}. Case insensitive; multiple options delimited by comma or space possible." "") +set(AVALAIBLE_SANITIZERS "address;leak;memory;thread;undefined") +OPTION(USE_SANITIZER "Enable sanitizer(s). Options are: ${AVALAIBLE_SANITIZERS}. Case insensitive; multiple options delimited by comma or space possible." "") string(TOLOWER "${USE_SANITIZER}" USE_SANITIZER) -if(USE_SANITIZER AND NOT (CMAKE_BUILD_TYPE IN_LIST "Debug;RelWithDebInfo")) - message(FATAL_ERROR "❌ Sanitizer only supported in Debug and RelWithDebInfo build types. Current: ${CMAKE_BUILD_TYPE}") +if((CMAKE_BUILD_TYPE IN_LIST "Debug;RelWithDebInfo") AND USE_SANITIZER) + message(FATAL_ERROR "❌ Sanitizer only supported in Debug and RelWithDebInfo build types.") endif() if(USE_SANITIZER) @@ -11,34 +11,37 @@ if(USE_SANITIZER) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") if(USE_SANITIZER MATCHES "address") - string(APPEND CMAKE_CXX_FLAGS " /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION") - string(APPEND CMAKE_EXE_LINKER_FLAGS " /fsanitize=address") + list(APPEND SANITIZER_COMPILE_OPTIONS /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION) else() message(FATAL_ERROR "❌ Sanitizer not supported by MSVC: ${USE_SANITIZER}. It only supports 'address'.") endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") if(USE_SANITIZER MATCHES "address") - string(APPEND CMAKE_CXX_FLAGS " /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION") - string(APPEND CMAKE_EXE_LINKER_FLAGS " /fsanitize=address") + list(APPEND SANITIZER_COMPILE_OPTIONS /fsanitize=address /D_DISABLE_VECTOR_ANNOTATION /D_DISABLE_STRING_ANNOTATION) + list(APPEND SANITIZER_LINK_LIBRARIES clang_rt.asan_dynamic-x86_64 clang_rt.asan_dynamic_runtime_thunk-x86_64) else() message(FATAL_ERROR "❌ Sanitizer not supported by Clang-MSVC: ${USE_SANITIZER}. It only supports 'address'.") endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") foreach(sanitizer ${USE_SANITIZER}) - if(NOT ${sanitizer} IN_LIST AVAILABLE_SANITIZERS) - message(FATAL_ERROR "❌ Sanitizer not supported: ${sanitizer}. It should be one of: ${AVAILABLE_SANITIZERS}.") + if(NOT ${sanitizer} IN_LIST AVALAIBLE_SANITIZERS) + message(FATAL_ERROR "❌ Sanitizer not supported: ${sanitizer}. It should be one of: ${AVALAIBLE_SANITIZERS}.") endif() - string(APPEND CMAKE_CXX_FLAGS " -fsanitize=${sanitizer}") - string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=${sanitizer}") - if(${sanitizer} MATCHES "memory") - string(APPEND CMAKE_CXX_FLAGS " -fsanitize-memory-track-origins -fPIE -pie") - string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize-memory-track-origins -fPIE -pie") + list(APPEND SANITIZER_COMPILE_OPTIONS -fsanitize=${sanitizer}) + list(APPEND SANITIZER_LINK_OPTIONS -fsanitize=${sanitizer}) + if (${sanitizer} MATCHES "memory") + list(APPEND SANITIZER_LINK_LIBRARIES -fsanitize-memory-track-origins -fPIE -pie) + list(APPEND SANITIZER_LINK_OPTIONS -fsanitize-memory-track-origins -fPIE -pie) endif() endforeach() - string(APPEND CMAKE_CXX_FLAGS " -fno-omit-frame-pointer") + list(APPEND SANITIZER_COMPILE_OPTIONS -fno-omit-frame-pointer) else() message(FATAL_ERROR "❌ Sanitizer: Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") endif() + list(REMOVE_DUPLICATES SANITIZER_COMPILE_OPTIONS) + list(REMOVE_DUPLICATES SANITIZER_LINK_OPTIONS) + list(REMOVE_DUPLICATES SANITIZER_LINK_LIBRARIES) + message(STATUS "🔍 Using sanitizer: ${USE_SANITIZER}") endif()