diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 622c678..3d3b5c4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,11 +10,11 @@ jobs: fail-fast: false matrix: build_type: [Release, Debug] - os: [ubuntu-20.04, ubuntu-18.04] + os: [ubuntu-22.04, ubuntu-24.04] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install dependencies - run: sudo apt-get install cmake + run: sudo apt-get install -y cmake systemtap-sdt-dev - name: cmake run: cmake -B builddir -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - name: make @@ -28,11 +28,11 @@ jobs: fail-fast: false matrix: build_type: [Release, Debug] - os: [ubuntu-20.04] + os: [ubuntu-22.04, ubuntu-24.04] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install dependencies - run: sudo apt-get install cmake clang systemtap-sdt-dev + run: sudo apt-get install -y cmake clang libomp-dev systemtap-sdt-dev - name: cmake run: CC=clang CXX=clang++ cmake -B builddir -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} - name: make diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ec9e7f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build* diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fe352b..cb89ab5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,58 @@ -cmake_minimum_required(VERSION 3.1) -set(CMAKE_CXX_STANDARD 11) +cmake_minimum_required(VERSION 3.16) +project(sortstring C CXX) + include(CheckIncludeFile) -project(sortstring) -include_directories(src src/util) +option(ENABLE_GCC_ANALYZER "Enable GCC -fanalyzer static analysis on internal sources" OFF) +if(ENABLE_GCC_ANALYZER) + if(NOT CMAKE_C_COMPILER_ID STREQUAL "GNU" OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(FATAL_ERROR "ENABLE_GCC_ANALYZER requires GCC for both C and C++ " + "(got C=${CMAKE_C_COMPILER_ID}, CXX=${CMAKE_CXX_COMPILER_ID}).") + endif() + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 13 OR CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) + message(FATAL_ERROR "ENABLE_GCC_ANALYZER requires GCC >= 13 (got " + "C=${CMAKE_C_COMPILER_VERSION}, CXX=${CMAKE_CXX_COMPILER_VERSION}).") + endif() +endif() + +option(ENABLE_CLANG_ANALYZER "Enable clang-tidy clang-analyzer-* checks on internal sources" OFF) +if(ENABLE_CLANG_ANALYZER) + find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy) + if(NOT CLANG_TIDY_EXECUTABLE) + message(FATAL_ERROR "ENABLE_CLANG_ANALYZER requires clang-tidy on PATH.") + endif() + set(CLANG_TIDY_COMMAND + ${CLANG_TIDY_EXECUTABLE} + -checks=-*,clang-analyzer-* + -header-filter=^${CMAKE_SOURCE_DIR}/src/.*) +endif() + +set(MARCH "native" CACHE STRING + "Value passed to -march=. Override to target a different ISA level.") + +check_include_file(sys/sdt.h HAVE_SYS_SDT_H) + +find_package(OpenMP REQUIRED) + +add_compile_options(-Wall -Wextra -march=${MARCH}) -link_libraries(rt) +string(APPEND CMAKE_C_FLAGS_RELEASE " -g") +string(APPEND CMAKE_CXX_FLAGS_RELEASE " -g") + +string(APPEND CMAKE_C_FLAGS_DEBUG " -O1 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") +string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O1 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") set(INTERNAL_SRCS - src/funnelsort.cpp + src/funnelsort_bfs_128way.cpp + src/funnelsort_bfs_16way.cpp + src/funnelsort_bfs_32way.cpp + src/funnelsort_bfs_64way.cpp + src/funnelsort_bfs_8way.cpp + src/funnelsort_dfs_128way.cpp + src/funnelsort_dfs_16way.cpp + src/funnelsort_dfs_32way.cpp + src/funnelsort_dfs_64way.cpp + src/funnelsort_dfs_8way.cpp src/msd_a.cpp src/msd_a2.cpp src/msd_lsd.cpp @@ -28,10 +72,10 @@ set(INTERNAL_SRCS src/mergesort_unstable.cpp src/mergesort_losertree.cpp src/mergesort_lcp.cpp - src/routines.c - src/util/timing.c - src/util/cpus_allowed.c - src/util/vmainfo.c) + src/routines.cpp + src/util/timing.cpp + src/util/cpus_allowed.cpp + src/util/vmainfo.cpp) set(EXTERNAL_SRCS external/lcp-quicksort.cpp @@ -50,26 +94,45 @@ set(EXTERNAL_SRCS external/forward16.c external/parallel_string_radix_sort.cpp) -check_include_file(sys/sdt.h HAVE_SYS_SDT_H) -if(HAVE_SYS_SDT_H) - add_definitions(-DHAVE_SYS_SDT_H=1) -endif() - -set_source_files_properties(external/adaptive.c PROPERTIES COMPILE_FLAGS -Wno-sign-compare) +set_source_files_properties(external/adaptive.c PROPERTIES COMPILE_FLAGS -Wno-sign-compare) set_source_files_properties(external/quicksort.c PROPERTIES COMPILE_FLAGS -Wno-sign-compare) -add_executable(sortstring src/sortstring.c ${INTERNAL_SRCS} ${EXTERNAL_SRCS}) +add_library(sortstring_internal OBJECT ${INTERNAL_SRCS}) +add_library(sortstring_internal_unittest OBJECT ${INTERNAL_SRCS}) +target_compile_definitions(sortstring_internal_unittest PRIVATE UNIT_TEST) + +foreach(tgt sortstring_internal sortstring_internal_unittest) + target_include_directories(${tgt} PUBLIC + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/util) + target_compile_definitions(${tgt} PUBLIC restrict=__restrict__) + if(HAVE_SYS_SDT_H) + target_compile_definitions(${tgt} PUBLIC HAVE_SYS_SDT_H=1) + endif() + target_link_libraries(${tgt} PUBLIC OpenMP::OpenMP_C OpenMP::OpenMP_CXX) + set_target_properties(${tgt} PROPERTIES + C_STANDARD 99 C_STANDARD_REQUIRED YES + CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +endforeach() + +if(ENABLE_GCC_ANALYZER) + target_compile_options(sortstring_internal PRIVATE -fanalyzer) + target_compile_options(sortstring_internal_unittest PRIVATE -fanalyzer) +endif() + +if(ENABLE_CLANG_ANALYZER) + set_target_properties(sortstring_internal sortstring_internal_unittest PROPERTIES + C_CLANG_TIDY "${CLANG_TIDY_COMMAND}" + CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") +endif() -add_executable(unit-test unit-test/main.cpp ${INTERNAL_SRCS} ${EXTERNAL_SRCS}) -target_compile_definitions(unit-test PUBLIC UNIT_TEST) +add_executable(sortstring src/sortstring.cpp ${EXTERNAL_SRCS}) +target_link_libraries(sortstring PRIVATE sortstring_internal rt) -add_definitions(-Drestrict=__restrict__) -set(CMAKE_CXX_FLAGS_RELEASE "-fopenmp -g -DNDEBUG -march=native ${CMAKE_CXX_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fopenmp -g -DNDEBUG -march=native ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") -set(CMAKE_C_FLAGS_RELEASE "-fopenmp -g -DNDEBUG -march=native ${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "-fopenmp -g -DNDEBUG -march=native ${CMAKE_C_FLAGS_RELWITHDEBINFO}") -set(CMAKE_CXX_FLAGS "-Wall -Wextra ${CMAKE_CXX_FLAGS}") -set(CMAKE_C_FLAGS "-Wall -Wextra -std=c99 ${CMAKE_C_FLAGS}") +add_executable(unit-test unit-test/main.cpp ${EXTERNAL_SRCS}) +target_link_libraries(unit-test PRIVATE sortstring_internal_unittest rt) +target_compile_definitions(unit-test PRIVATE UNIT_TEST) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -g -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O1 -g -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2") +set_target_properties(sortstring unit-test PROPERTIES + C_STANDARD 99 C_STANDARD_REQUIRED YES + CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) diff --git a/README.md b/README.md index f60cbc8..4f0ab60 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,10 @@ purposes, and are copyright by their respective authors. Requirements ------------ - * C++11 - * CMake + * C++17 + * CMake >= 3.16 + * OpenMP + * Ninja (optional; the default Make generator also works) Compilation @@ -49,18 +51,56 @@ Compilation Default compilation with GCC: - $ git clone git://github.com/rantala/string-sorting.git - $ mkdir string-sorting-build - $ cd string-sorting-build - $ cmake -DCMAKE_BUILD_TYPE=Release ../string-sorting - $ make - $ ./sortstring + $ git clone https://github.com/rantala/string-sorting.git + $ cd string-sorting + $ cmake -B build -G Ninja && ninja -C build + $ ./build/sortstring Use a separate debug build for easier debugging: - $ mkdir debug-build - $ cd debug-build - $ cmake -DCMAKE_BUILD_TYPE=Debug ../string-sorting + $ cmake -B build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug && ninja -C build-debug + + +GCC static analyzer +------------------- + +The build can be configured to run the GCC static analyzer (`-fanalyzer`) on +the project's own sources. Third-party code under `external/` is excluded. + + $ cmake -B build-gcc-analyzer -G Ninja -DENABLE_GCC_ANALYZER=ON && ninja -C build-gcc-analyzer + +The option requires GCC >= 13 for both C and C++; configuration fails fast +with any other compiler or older version. Analyzer diagnostics are emitted as +build warnings; the build still succeeds. Expect significantly longer compile +times when the option is enabled. + + +Clang static analyzer +--------------------- + +The build can also be configured to run the Clang static analyzer via +`clang-tidy`, scoped to the `clang-analyzer-*` check group. Third-party code +under `external/` is excluded. + + $ cmake -B build-clang-analyzer -G Ninja -DENABLE_CLANG_ANALYZER=ON && ninja -C build-clang-analyzer + +The option requires `clang-tidy` on `PATH` (Ubuntu: `apt install clang-tidy`); +configuration fails fast if it is not found. Diagnostics appear inline like +compiler warnings and the build still succeeds. The configured C/C++ compiler +does not need to be Clang. `ENABLE_GCC_ANALYZER` and `ENABLE_CLANG_ANALYZER` +can be combined when building with GCC >= 13. + + +Target architecture +------------------- + +The compiler `-march=` value can be overridden via the `MARCH` cache +variable (default `native`): + + $ cmake -B build-v3 -G Ninja -DMARCH=x86-64-v3 && ninja -C build-v3 + +Useful for targeting a portable ISA level or restricting the instructions +emitted by the compiler. Huge pages diff --git a/src/burstsort_mkq.cpp b/src/burstsort_mkq.cpp index 53d7be0..c0cd44f 100644 --- a/src/burstsort_mkq.cpp +++ b/src/burstsort_mkq.cpp @@ -254,6 +254,7 @@ burst_insert(TSTNode* root, unsigned char** strings, size_t N) } CharT* oracle = static_cast( malloc(buck->size()*sizeof(CharT))); + if (!oracle) abort(); for (unsigned j=0; j < buck->size(); ++j) { oracle[j] = get_char((*buck)[j], depth); } @@ -327,6 +328,7 @@ template static inline void burstsort_mkq_simpleburst(unsigned char** strings, size_t N) { + if (N == 0) return; typedef std::vector BucketT; typedef BurstSimple BurstImpl; TSTNode root; @@ -355,6 +357,7 @@ template static inline void burstsort_mkq_recursiveburst(unsigned char** strings, size_t N) { + if (N == 0) return; typedef std::vector BucketT; typedef BurstRecursive BurstImpl; TSTNode root; diff --git a/src/funnelsort_bfs_128way.cpp b/src/funnelsort_bfs_128way.cpp new file mode 100644 index 0000000..3226595 --- /dev/null +++ b/src/funnelsort_bfs_128way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<128, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_128way_bfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<128, buffer_layout_bfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_128way_bfs, + "funnelsort_128way_bfs") diff --git a/src/funnelsort_bfs_16way.cpp b/src/funnelsort_bfs_16way.cpp new file mode 100644 index 0000000..d1d41d6 --- /dev/null +++ b/src/funnelsort_bfs_16way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<16, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_16way_bfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<16, buffer_layout_bfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_16way_bfs, + "funnelsort_16way_bfs") diff --git a/src/funnelsort_bfs_32way.cpp b/src/funnelsort_bfs_32way.cpp new file mode 100644 index 0000000..01ae016 --- /dev/null +++ b/src/funnelsort_bfs_32way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<32, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_32way_bfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<32, buffer_layout_bfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_32way_bfs, + "funnelsort_32way_bfs") diff --git a/src/funnelsort_bfs_64way.cpp b/src/funnelsort_bfs_64way.cpp new file mode 100644 index 0000000..fd9bd3f --- /dev/null +++ b/src/funnelsort_bfs_64way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<64, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_64way_bfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<64, buffer_layout_bfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_64way_bfs, + "funnelsort_64way_bfs") diff --git a/src/funnelsort_bfs_8way.cpp b/src/funnelsort_bfs_8way.cpp new file mode 100644 index 0000000..725f9d3 --- /dev/null +++ b/src/funnelsort_bfs_8way.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template <> void funnelsort<4, buffer_layout_bfs>(unsigned char** strings, + size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } + +template void funnelsort<8, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_8way_bfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<8, buffer_layout_bfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_8way_bfs, + "funnelsort_8way_bfs") diff --git a/src/funnelsort_dfs_128way.cpp b/src/funnelsort_dfs_128way.cpp new file mode 100644 index 0000000..88a3e4a --- /dev/null +++ b/src/funnelsort_dfs_128way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<128, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_128way_dfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<128, buffer_layout_dfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_128way_dfs, + "funnelsort_128way_dfs") diff --git a/src/funnelsort_dfs_16way.cpp b/src/funnelsort_dfs_16way.cpp new file mode 100644 index 0000000..7517b50 --- /dev/null +++ b/src/funnelsort_dfs_16way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<16, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_16way_dfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<16, buffer_layout_dfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_16way_dfs, + "funnelsort_16way_dfs") diff --git a/src/funnelsort_dfs_32way.cpp b/src/funnelsort_dfs_32way.cpp new file mode 100644 index 0000000..575b023 --- /dev/null +++ b/src/funnelsort_dfs_32way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<32, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_32way_dfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<32, buffer_layout_dfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_32way_dfs, + "funnelsort_32way_dfs") diff --git a/src/funnelsort_dfs_64way.cpp b/src/funnelsort_dfs_64way.cpp new file mode 100644 index 0000000..fd8d732 --- /dev/null +++ b/src/funnelsort_dfs_64way.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template void funnelsort<64, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_64way_dfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<64, buffer_layout_dfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_64way_dfs, + "funnelsort_64way_dfs") diff --git a/src/funnelsort_dfs_8way.cpp b/src/funnelsort_dfs_8way.cpp new file mode 100644 index 0000000..ff804ee --- /dev/null +++ b/src/funnelsort_dfs_8way.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2008 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "funnelsort_impl.h" + +template <> void funnelsort<4, buffer_layout_dfs>(unsigned char** strings, + size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } + +template void funnelsort<8, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +void funnelsort_8way_dfs(unsigned char** strings, size_t n) +{ funnelsort_Kway<8, buffer_layout_dfs>(strings, n); } + +ROUTINE_REGISTER_SINGLECORE(funnelsort_8way_dfs, + "funnelsort_8way_dfs") diff --git a/src/funnelsort.cpp b/src/funnelsort_impl.h similarity index 90% rename from src/funnelsort.cpp rename to src/funnelsort_impl.h index 673b2e8..6f760ea 100644 --- a/src/funnelsort.cpp +++ b/src/funnelsort_impl.h @@ -77,6 +77,9 @@ * } */ +#ifndef FUNNELSORT_IMPL_H +#define FUNNELSORT_IMPL_H + #include "routine.h" #include "util/debug.h" #include "util/insertion_sort.h" @@ -650,7 +653,7 @@ struct fill<16,I,BufferLayout> // splitting the input into K streams, and using a fixed size K-merger. // Then use K/4 or K/2 on the next level of recursion. template class BufferLayout> -static void +void funnelsort(unsigned char** strings, size_t n, unsigned char** restrict tmp) { debug() << __func__ << "(), n=" << n << "\n"; @@ -683,64 +686,36 @@ funnelsort(unsigned char** strings, size_t n, unsigned char** restrict tmp) // Switch to 4-way mergesort on small inputs/lower levels of recursion. void mergesort_4way(unsigned char**, size_t, unsigned char**); -//template <> void funnelsort<2,buffer_layout_bfs>(unsigned char** strings, -// size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } -//template <> void funnelsort<2,buffer_layout_dfs>(unsigned char** strings, -// size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } -template <> void funnelsort<4,buffer_layout_bfs>(unsigned char** strings, - size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } -template <> void funnelsort<4,buffer_layout_dfs>(unsigned char** strings, - size_t n, unsigned char** tmp) { mergesort_4way(strings, n, tmp); } + +template <> void funnelsort<4, buffer_layout_bfs>( + unsigned char**, size_t, unsigned char**); +template <> void funnelsort<4, buffer_layout_dfs>( + unsigned char**, size_t, unsigned char**); + +#define FUNNELSORT_EXTERN(K, LAYOUT) \ + extern template void funnelsort( \ + unsigned char**, size_t, unsigned char**); +FUNNELSORT_EXTERN(8, buffer_layout_bfs) +FUNNELSORT_EXTERN(16, buffer_layout_bfs) +FUNNELSORT_EXTERN(32, buffer_layout_bfs) +FUNNELSORT_EXTERN(64, buffer_layout_bfs) +FUNNELSORT_EXTERN(128, buffer_layout_bfs) +FUNNELSORT_EXTERN(8, buffer_layout_dfs) +FUNNELSORT_EXTERN(16, buffer_layout_dfs) +FUNNELSORT_EXTERN(32, buffer_layout_dfs) +FUNNELSORT_EXTERN(64, buffer_layout_dfs) +FUNNELSORT_EXTERN(128, buffer_layout_dfs) +#undef FUNNELSORT_EXTERN template class BufferLayout> void funnelsort_Kway(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); funnelsort(strings, n, tmp); free(tmp); } -void funnelsort_8way_bfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<8, buffer_layout_bfs>(strings, n); } -void funnelsort_16way_bfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<16, buffer_layout_bfs>(strings, n); } -void funnelsort_32way_bfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<32, buffer_layout_bfs>(strings, n); } -void funnelsort_64way_bfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<64, buffer_layout_bfs>(strings, n); } -void funnelsort_128way_bfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<128, buffer_layout_bfs>(strings, n); } - -void funnelsort_8way_dfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<8, buffer_layout_dfs>(strings, n); } -void funnelsort_16way_dfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<16, buffer_layout_dfs>(strings, n); } -void funnelsort_32way_dfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<32, buffer_layout_dfs>(strings, n); } -void funnelsort_64way_dfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<64, buffer_layout_dfs>(strings, n); } -void funnelsort_128way_dfs(unsigned char** strings, size_t n) -{ funnelsort_Kway<128, buffer_layout_dfs>(strings, n); } - -ROUTINE_REGISTER_SINGLECORE(funnelsort_8way_bfs, - "funnelsort_8way_bfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_16way_bfs, - "funnelsort_16way_bfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_32way_bfs, - "funnelsort_32way_bfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_64way_bfs, - "funnelsort_64way_bfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_128way_bfs, - "funnelsort_128way_bfs") - -ROUTINE_REGISTER_SINGLECORE(funnelsort_8way_dfs, - "funnelsort_8way_dfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_16way_dfs, - "funnelsort_16way_dfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_32way_dfs, - "funnelsort_32way_dfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_64way_dfs, - "funnelsort_64way_dfs") -ROUTINE_REGISTER_SINGLECORE(funnelsort_128way_dfs, - "funnelsort_128way_dfs") +#endif diff --git a/src/losertree.h b/src/losertree.h index 315ad8f..cb16bb1 100644 --- a/src/losertree.h +++ b/src/losertree.h @@ -83,6 +83,7 @@ struct loser_tree assert(_nonempty_streams>1); void* raw = malloc(_stream_offset*sizeof(unsigned) + _stream_offset*sizeof(Stream)); + if (!raw) abort(); _nodes = static_cast(raw); _streams = reinterpret_cast( static_cast(raw) + diff --git a/src/mergesort.cpp b/src/mergesort.cpp index d38f1d6..ab12df0 100644 --- a/src/mergesort.cpp +++ b/src/mergesort.cpp @@ -113,8 +113,10 @@ mergesort_2way(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_2way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_2way(strings, n, tmp); free(tmp); } @@ -142,8 +144,10 @@ mergesort_2way_parallel(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_2way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_2way_parallel(strings, n, tmp); free(tmp); } @@ -315,8 +319,10 @@ mergesort_3way(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_3way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_3way(strings, n, tmp); free(tmp); } @@ -348,8 +354,10 @@ mergesort_3way_parallel(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_3way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_3way_parallel(strings, n, tmp); free(tmp); } @@ -478,8 +486,10 @@ mergesort_4way(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_4way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_4way(strings, n, tmp); free(tmp); } @@ -516,8 +526,10 @@ mergesort_4way_parallel(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_4way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_4way_parallel(strings, n, tmp); free(tmp); } diff --git a/src/mergesort_lcp.cpp b/src/mergesort_lcp.cpp index 352e86a..dd06ff3 100644 --- a/src/mergesort_lcp.cpp +++ b/src/mergesort_lcp.cpp @@ -263,10 +263,14 @@ mergesort_lcp_2way(unsigned char** restrict strings_input, void mergesort_lcp_2way(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_input) abort(); lcp_t* lcp_output = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_output) abort(); unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); const MergeResult m = mergesort_lcp_2way(strings, tmp, lcp_input, lcp_output, n); if (m == SortedInTemp) { @@ -334,10 +338,14 @@ mergesort_lcp_2way_parallel( void mergesort_lcp_2way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_input) abort(); lcp_t* lcp_output = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_output) abort(); unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); #pragma omp parallel { #pragma omp single @@ -818,11 +826,15 @@ mergesort_lcp_3way(unsigned char** restrict strings_input, void mergesort_lcp_3way(unsigned char** strings, size_t n) { + if (n == 0) return; debug() << __func__ << '\n'; lcp_t* lcp_input = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_input) abort(); lcp_t* lcp_tmp = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_tmp) abort(); unsigned char** input_tmp = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!input_tmp) abort(); const MergeResult m = mergesort_lcp_3way(strings, input_tmp, lcp_input, lcp_tmp, n); if (m == SortedInTemp) { @@ -940,11 +952,15 @@ mergesort_lcp_3way_parallel(unsigned char** restrict strings_input, void mergesort_lcp_3way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; debug() << __func__ << '\n'; lcp_t* lcp_input = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_input) abort(); lcp_t* lcp_tmp = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_tmp) abort(); unsigned char** input_tmp = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!input_tmp) abort(); #pragma omp parallel { #pragma omp single @@ -1407,11 +1423,17 @@ template static void mergesort_cache_lcp_2way(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_input) abort(); lcp_t* lcp_tmp = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_tmp) abort(); unsigned char** input_tmp = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!input_tmp) abort(); CharT* cache = (CharT*) malloc(n*sizeof(CharT)); + if (!cache) abort(); CharT* cache_tmp = (CharT*) malloc(n*sizeof(CharT)); + if (!cache_tmp) abort(); MergeResult m = mergesort_cache_lcp_2way(strings, input_tmp, lcp_input, lcp_tmp, cache, cache_tmp, n); if (m == SortedInTemp) { @@ -1523,11 +1545,17 @@ template static void mergesort_cache_lcp_2way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_input) abort(); lcp_t* lcp_tmp = (lcp_t*) malloc(n*sizeof(lcp_t)); + if (!lcp_tmp) abort(); unsigned char** input_tmp = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!input_tmp) abort(); CharT* cache = (CharT*) malloc(n*sizeof(CharT)); + if (!cache) abort(); CharT* cache_tmp = (CharT*) malloc(n*sizeof(CharT)); + if (!cache_tmp) abort(); MergeResult m = mergesort_cache_lcp_2way_parallel(strings, input_tmp, lcp_input, lcp_tmp, cache, cache_tmp, n); if (m == SortedInTemp) { @@ -1737,10 +1765,14 @@ mergesort_lcp_2way_unstable(unsigned char** restrict strings_input, void mergesort_lcp_2way_unstable(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_input) abort(); lcp_t* lcp_output = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_output) abort(); unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); const MergeResult m = mergesort_lcp_2way_unstable(strings, tmp, lcp_input, lcp_output, n); if (m == SortedInTemp) { @@ -1810,10 +1842,14 @@ mergesort_lcp_2way_unstable_parallel(unsigned char** restrict strings_input, void mergesort_lcp_2way_unstable_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; lcp_t* lcp_input = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_input) abort(); lcp_t* lcp_output = static_cast(malloc(n*sizeof(lcp_t))); + if (!lcp_output) abort(); unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); #pragma omp parallel { #pragma omp single diff --git a/src/mergesort_losertree.cpp b/src/mergesort_losertree.cpp index 8a2a473..b20db3c 100644 --- a/src/mergesort_losertree.cpp +++ b/src/mergesort_losertree.cpp @@ -69,36 +69,46 @@ mergesort_losertree(unsigned char** strings, size_t n, unsigned char** tmp) void mergesort_losertree_64way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree<64>(strings, n, tmp); free(tmp); } void mergesort_losertree_128way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree<128>(strings, n, tmp); free(tmp); } void mergesort_losertree_256way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree<256>(strings, n, tmp); free(tmp); } void mergesort_losertree_512way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree<512>(strings, n, tmp); free(tmp); } void mergesort_losertree_1024way(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree<1024>(strings, n, tmp); free(tmp); } @@ -144,36 +154,46 @@ mergesort_losertree_parallel(unsigned char** strings, size_t n, unsigned char** void mergesort_losertree_64way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree_parallel<64>(strings, n, tmp); free(tmp); } void mergesort_losertree_128way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree_parallel<128>(strings, n, tmp); free(tmp); } void mergesort_losertree_256way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree_parallel<256>(strings, n, tmp); free(tmp); } void mergesort_losertree_512way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree_parallel<512>(strings, n, tmp); free(tmp); } void mergesort_losertree_1024way_parallel(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_losertree_parallel<1024>(strings, n, tmp); free(tmp); } diff --git a/src/mergesort_unstable.cpp b/src/mergesort_unstable.cpp index e8bb30d..afa1560 100644 --- a/src/mergesort_unstable.cpp +++ b/src/mergesort_unstable.cpp @@ -147,8 +147,10 @@ mergesort_2way_unstable(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_2way_unstable(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_2way_unstable(strings, n, tmp); free(tmp); } @@ -328,8 +330,10 @@ mergesort_3way_unstable(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_3way_unstable(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_3way_unstable(strings, n, tmp); free(tmp); } @@ -1871,8 +1875,10 @@ mergesort_4way_unstable(unsigned char** strings, size_t n, unsigned char** tmp) } void mergesort_4way_unstable(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** tmp = static_cast( malloc(n*sizeof(unsigned char*))); + if (!tmp) abort(); mergesort_4way_unstable(strings, n, tmp); free(tmp); } diff --git a/src/msd_a.cpp b/src/msd_a.cpp index edadb87..366197f 100644 --- a/src/msd_a.cpp +++ b/src/msd_a.cpp @@ -94,6 +94,7 @@ msd_A(cacheblock_t* cache, size_t N, size_t cache_depth, size_t true_depth) ++bucketsize[cache[i].bytes[cache_depth]]; cacheblock_t* sorted = (cacheblock_t*) malloc(N*sizeof(cacheblock_t)); + if (!sorted) abort(); static size_t bucketindex[256]; bucketindex[0] = 0; for (unsigned i=1; i < 256; ++i) @@ -127,6 +128,7 @@ msd_A_adaptive(cacheblock_t* cache, cache_depth = 0; } size_t* bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < N; ++i) { uint16_t bucket = (cache[i].bytes[cache_depth] << 8) | @@ -135,6 +137,7 @@ msd_A_adaptive(cacheblock_t* cache, } cacheblock_t* sorted = (cacheblock_t*) malloc(N*sizeof(cacheblock_t)); + if (!sorted) abort(); static size_t bucketindex[0x10000]; bucketindex[0] = 0; for (unsigned i=1; i < 0x10000; ++i) @@ -160,7 +163,9 @@ msd_A_adaptive(cacheblock_t* cache, void msd_A(unsigned char** strings, size_t N) { + if (N == 0) return; cacheblock_t* cache = (cacheblock_t*) malloc(N*sizeof(cacheblock_t)); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; fill_cache(cache, N, 0); msd_A(cache, N, 0, 0); @@ -172,7 +177,9 @@ ROUTINE_REGISTER_SINGLECORE(msd_A, "msd_A") void msd_A_adaptive(unsigned char** strings, size_t N) { + if (N == 0) return; cacheblock_t* cache = (cacheblock_t*) malloc(N*sizeof(cacheblock_t)); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; fill_cache(cache, N, 0); msd_A_adaptive(cache, N, 0, 0); diff --git a/src/msd_a2.cpp b/src/msd_a2.cpp index 5c80eac..3e07c41 100644 --- a/src/msd_a2.cpp +++ b/src/msd_a2.cpp @@ -117,6 +117,7 @@ struct TempSpace allocated = static_cast( malloc((elems-elements_in_strings) * sizeof(cacheblock_t))); + if (!allocated) abort(); } } void deallocate() @@ -196,6 +197,7 @@ msd_A2_adaptive(cacheblock_t* cache, tmp.allocate(N); size_t* bucketsize = static_cast(calloc(0x10000, sizeof(size_t))); + if (!bucketsize) abort(); for (size_t i=0; i < N; ++i) { uint16_t bucket = (cache[i].bytes[cache_depth] << 8) | @@ -226,8 +228,10 @@ msd_A2_adaptive(cacheblock_t* cache, void msd_A2(unsigned char** strings, size_t N) { + if (N == 0) return; cacheblock_t* cache = static_cast(malloc(N*sizeof(cacheblock_t))); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; TempSpace tmp(strings, N); fill_cache(cache, N, 0); @@ -240,8 +244,10 @@ ROUTINE_REGISTER_SINGLECORE(msd_A2, "msd_A2") void msd_A2_adaptive(unsigned char** strings, size_t N) { + if (N == 0) return; cacheblock_t* cache = static_cast(malloc(N*sizeof(cacheblock_t))); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; TempSpace tmp(strings, N); fill_cache(cache, N, 0); diff --git a/src/msd_ce.cpp b/src/msd_ce.cpp index 594b5a4..0fb623b 100644 --- a/src/msd_ce.cpp +++ b/src/msd_ce.cpp @@ -85,6 +85,7 @@ msd_CE0(unsigned char** strings, size_t n, size_t depth) ++bucketsize[strings[i][depth]]; unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); static size_t bucketindex[256]; bucketindex[0] = 0; for (size_t i=1; i < 256; ++i) @@ -115,10 +116,12 @@ msd_CE1(unsigned char** strings, size_t n, size_t depth) size_t bucketsize[256] = {0}; unsigned char* restrict oracle = (unsigned char*) malloc(n); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i] = strings[i][depth]]; unsigned char** restrict sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); size_t bucketindex[256]; bucketindex[0] = 0; for (size_t i=1; i < 256; ++i) @@ -150,12 +153,14 @@ msd_CE2(unsigned char** strings, size_t n, size_t depth) size_t bucketsize[256] = {0}; unsigned char* restrict oracle = (unsigned char*) malloc(n); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = strings[i][depth]; for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; unsigned char** restrict sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); size_t bucketindex[256]; bucketindex[0] = 0; for (size_t i=1; i < 256; ++i) @@ -187,12 +192,14 @@ msd_CE2_16bit(unsigned char** strings, size_t n, size_t depth) uint16_t bucketsize[256] = {0}; unsigned char* restrict oracle = (unsigned char*) malloc(n); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = strings[i][depth]; for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; unsigned char** restrict sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); uint16_t bucketindex[256]; bucketindex[0] = 0; for (size_t i=1; i < 256; ++i) @@ -219,14 +226,17 @@ msd_CE3(unsigned char** strings, size_t n, size_t depth) } uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = get_char(strings[i], depth); size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); static size_t bucketindex[0x10000]; bucketindex[0] = 0; for (size_t i=1; i < 0x10000; ++i) @@ -259,14 +269,17 @@ msd_CE4(unsigned char** strings, size_t n, size_t depth) } uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = get_char(strings[i], depth); size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); static size_t bucketindex[0x10000]; bucketindex[0] = 0; for (size_t i=1; i < 0x10000; ++i) @@ -332,6 +345,7 @@ msd_CE5(unsigned char** strings, size_t n, size_t depth, oracle[i] = get_char(strings[i], depth); size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; static size_t bucketindex[0x10000]; @@ -353,10 +367,13 @@ msd_CE5(unsigned char** strings, size_t n, size_t depth, void msd_CE5(unsigned char** strings, size_t n) { + if (n == 0) return; uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); msd_CE5(strings, n, 0, oracle, sorted); free(oracle); free(sorted); @@ -387,6 +404,7 @@ msd_CE6(unsigned char** strings, size_t n, size_t depth, } size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; static size_t bucketindex[0x10000]; @@ -407,10 +425,13 @@ msd_CE6(unsigned char** strings, size_t n, size_t depth, } void msd_CE6(unsigned char** strings, size_t n) { + if (n == 0) return; uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); msd_CE6(strings, n, 0, oracle, sorted); free(oracle); free(sorted); @@ -441,6 +462,7 @@ msd_CE7_(unsigned char** strings, size_t n, size_t depth, } size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); int is_sorted = 1; { size_t i; @@ -480,10 +502,13 @@ msd_CE7_(unsigned char** strings, size_t n, size_t depth, } void msd_CE7(unsigned char** strings, size_t n) { + if (n == 0) return; uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); msd_CE7_(strings, n, 0, oracle, sorted); free(oracle); free(sorted); @@ -517,6 +542,7 @@ msd_CE8_(unsigned char** strings, size_t n, size_t depth, } size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); int is_sorted = 1; { size_t i; @@ -564,10 +590,13 @@ msd_CE8_(unsigned char** strings, size_t n, size_t depth, } void msd_CE8(unsigned char** strings, size_t n) { + if (n == 0) return; uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); msd_CE8_(strings, n, 0, oracle, sorted); free(oracle); free(sorted); diff --git a/src/msd_ci.cpp b/src/msd_ci.cpp index 69d1643..6bcb9fa 100644 --- a/src/msd_ci.cpp +++ b/src/msd_ci.cpp @@ -56,6 +56,7 @@ msd_ci(unsigned char** strings, size_t n, size_t depth) BucketsizeType bucketsize[256] = {0}; unsigned char* restrict oracle = (unsigned char*) malloc(n); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = strings[i][depth]; for (size_t i=0; i < n; ++i) @@ -107,10 +108,12 @@ msd_ci_adaptive(unsigned char** strings, size_t n, size_t depth) } uint16_t* restrict oracle = (uint16_t*) malloc(n*sizeof(uint16_t)); + if (!oracle) abort(); for (size_t i=0; i < n; ++i) oracle[i] = get_char(strings[i], depth); size_t* restrict bucketsize = (size_t*) calloc(0x10000, sizeof(size_t)); + if (!bucketsize) abort(); for (size_t i=0; i < n; ++i) ++bucketsize[oracle[i]]; static ssize_t bucketindex[0x10000]; diff --git a/src/msd_dyn_vector.cpp b/src/msd_dyn_vector.cpp index ce8b723..11a805e 100644 --- a/src/msd_dyn_vector.cpp +++ b/src/msd_dyn_vector.cpp @@ -137,6 +137,7 @@ msd_D_adaptive(unsigned char** strings, size_t n, size_t depth, Bucket* buckets) return; } size_t* bucketsize = (size_t*) malloc(0x10000 * sizeof(size_t)); + if (!bucketsize) abort(); size_t i=0; for (; i < n-n%16; i+=16) { uint16_t cache[16]; diff --git a/src/msd_lsd.cpp b/src/msd_lsd.cpp index 3bb68bc..fb3a402 100644 --- a/src/msd_lsd.cpp +++ b/src/msd_lsd.cpp @@ -93,6 +93,7 @@ msd_lsd(Cacheblock* cache, size_t N, size_t depth) ++bucketsize[cache[i].chars[byte]]; Cacheblock* sorted = (Cacheblock*) malloc(N*sizeof(Cacheblock)); + if (!sorted) abort(); size_t bucketindex[256]; bucketindex[0] = 0; for (size_t i=1; i < 256; ++i) @@ -142,6 +143,7 @@ msd_lsd_adaptive(Cacheblock* cache, size_t N, size_t depth) } Cacheblock* sorted = (Cacheblock*) malloc(N*sizeof(Cacheblock)); + if (!sorted) abort(); static size_t bucketindex[0x10000]; bucketindex[0] = 0; for (size_t i=1; i < 0x10000; ++i) @@ -178,8 +180,10 @@ template static void msd_A_lsd(unsigned char** strings, size_t N) { + if (N == 0) return; Cacheblock* cache = static_cast*>( malloc(N*sizeof(Cacheblock))); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; fill_cache(cache, N, 0); msd_lsd(cache, N, 0); @@ -191,8 +195,10 @@ template static void msd_A_lsd_adaptive(unsigned char** strings, size_t N) { + if (N == 0) return; Cacheblock* cache = static_cast*>( malloc(N*sizeof(Cacheblock))); + if (!cache) abort(); for (size_t i=0; i < N; ++i) cache[i].ptr = strings[i]; fill_cache(cache, N, 0); msd_lsd_adaptive(cache, N, 0); diff --git a/src/multikey_cache.cpp b/src/multikey_cache.cpp index a945a0c..281133c 100644 --- a/src/multikey_cache.cpp +++ b/src/multikey_cache.cpp @@ -218,6 +218,7 @@ multikey_cache(unsigned char** strings, size_t n, size_t depth) Cacheblock* cache = static_cast*>( malloc(n*sizeof(Cacheblock))); + if (!cache) abort(); for (size_t i=0; i < n; ++i) { cache[i].ptr = strings[i]; } diff --git a/src/multikey_multipivot.cpp b/src/multikey_multipivot.cpp index 11d7bcc..fe8df5d 100644 --- a/src/multikey_multipivot.cpp +++ b/src/multikey_multipivot.cpp @@ -403,6 +403,7 @@ multikey_multipivot(unsigned char** strings, size_t n, size_t depth) pivots[i] = sample_array[step*i]; } uint8_t* restrict oracle = static_cast(_mm_malloc(n, 16)); + if (!oracle) abort(); fill_oracle(strings, n, oracle, pivots, depth); std::array bucketsize; bucketsize.fill(0); @@ -423,6 +424,7 @@ multikey_multipivot(unsigned char** strings, size_t n, size_t depth) if (not sorted) { unsigned char** sorted = (unsigned char**) malloc(n*sizeof(unsigned char*)); + if (!sorted) abort(); static std::array bucketindex; bucketindex[0] = 0; for (unsigned i=1; i < total_buckets(Pivots); ++i) diff --git a/src/multikey_simd.cpp b/src/multikey_simd.cpp index 336c63f..a77dec3 100644 --- a/src/multikey_simd.cpp +++ b/src/multikey_simd.cpp @@ -282,6 +282,7 @@ multikey_simd(unsigned char** strings, size_t N, size_t depth) CharT partval = pseudo_median(strings, N, depth); uint8_t* const restrict oracle = static_cast(_mm_malloc(N, 16)); + if (!oracle) abort(); std::array bucketsize; bucketsize.fill(0); size_t i=N-N%16; @@ -295,6 +296,7 @@ multikey_simd(unsigned char** strings, size_t N, size_t depth) assert(bucketsize[0] + bucketsize[1] + bucketsize[2] == N); unsigned char** sorted = static_cast(malloc(N*sizeof(unsigned char*))); + if (!sorted) abort(); size_t bucketindex[3]; bucketindex[0] = 0; bucketindex[1] = bucketsize[0]; @@ -377,10 +379,13 @@ multikey_simd_b(unsigned char** strings, size_t N, size_t depth, void multikey_simd_b_1(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** sorted = static_cast(malloc(n*sizeof(unsigned char*))); + if (!sorted) abort(); uint8_t* const restrict oracle = static_cast(_mm_malloc(n, 16)); + if (!oracle) abort(); multikey_simd_b(strings, n, 0, sorted, oracle); _mm_free(oracle); free(sorted); @@ -388,10 +393,13 @@ void multikey_simd_b_1(unsigned char** strings, size_t n) void multikey_simd_b_2(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** sorted = static_cast(malloc(n*sizeof(unsigned char*))); + if (!sorted) abort(); uint8_t* const restrict oracle = static_cast(_mm_malloc(n, 16)); + if (!oracle) abort(); multikey_simd_b(strings, n, 0, sorted, oracle); _mm_free(oracle); free(sorted); @@ -399,10 +407,13 @@ void multikey_simd_b_2(unsigned char** strings, size_t n) void multikey_simd_b_4(unsigned char** strings, size_t n) { + if (n == 0) return; unsigned char** sorted = static_cast(malloc(n*sizeof(unsigned char*))); + if (!sorted) abort(); uint8_t* const restrict oracle = static_cast(_mm_malloc(n, 16)); + if (!oracle) abort(); multikey_simd_b(strings, n, 0, sorted, oracle); _mm_free(oracle); free(sorted); @@ -426,6 +437,7 @@ multikey_simd_parallel(unsigned char** strings, size_t N, size_t depth) CharT partval = pseudo_median(strings, N, depth); uint8_t* const restrict oracle = static_cast(_mm_malloc(N, 16)); + if (!oracle) abort(); std::array bucketsize; bucketsize.fill(0); size_t i=N-N%32; @@ -450,6 +462,7 @@ multikey_simd_parallel(unsigned char** strings, size_t N, size_t depth) assert(bucketsize[0] + bucketsize[1] + bucketsize[2] == N); unsigned char** sorted = static_cast(malloc(N*sizeof(unsigned char*))); + if (!sorted) abort(); size_t bucketindex[3]; bucketindex[0] = 0; bucketindex[1] = bucketsize[0]; diff --git a/src/routines.c b/src/routines.cpp similarity index 58% rename from src/routines.c rename to src/routines.cpp index d9fa44a..8184c3c 100644 --- a/src/routines.c +++ b/src/routines.cpp @@ -20,56 +20,52 @@ * IN THE SOFTWARE. */ -#include "routine.h" -#include +#include "routines.h" -#define ROUTINES_MAX 256 +#include +#include +#include -static const struct routine *routines[ROUTINES_MAX]; -static unsigned routine_cnt; +namespace { -void -routine_register(const struct routine *r) +constexpr unsigned ROUTINES_MAX = 256; + +const routine *routines[ROUTINES_MAX]; +unsigned routine_cnt; + +} // namespace + +extern "C" void +routine_register(const routine *r) { if (!r) - abort(); + std::abort(); if (!r->name) - abort(); + std::abort(); if (!r->desc) - abort(); + std::abort(); if (routine_cnt >= ROUTINES_MAX) - abort(); + std::abort(); routines[routine_cnt++] = r; } -const struct routine * -routine_from_name(const char *name) +std::optional +routine_from_name(std::string_view name) { - unsigned i; - for (i=0; i < routine_cnt; ++i) - if (strcmp(name, routines[i]->name) == 0) + for (unsigned i = 0; i < routine_cnt; ++i) + if (name == routines[i]->name) return routines[i]; - return NULL; -} - -static int -routine_cmp(const void *a, const void *b) -{ - const struct routine *aa = *(const struct routine **)a; - const struct routine *bb = *(const struct routine **)b; - if (aa->f == bb->f) - return 0; - if (aa->multicore < bb->multicore) - return -1; - if (aa->multicore > bb->multicore) - return 1; - return strcmp(aa->name, bb->name); + return std::nullopt; } -void -routine_get_all(const struct routine ***r, unsigned *cnt) +std::pair +routine_get_all() { - *r = routines; - *cnt = routine_cnt; - qsort(*r, *cnt, sizeof(struct routine *), routine_cmp); + std::sort(routines, routines + routine_cnt, + [](const routine *a, const routine *b) { + if (a->multicore != b->multicore) + return a->multicore < b->multicore; + return std::strcmp(a->name, b->name) < 0; + }); + return {routines, routine_cnt}; } diff --git a/src/routines.h b/src/routines.h index 1c43f3a..00a6878 100644 --- a/src/routines.h +++ b/src/routines.h @@ -25,15 +25,11 @@ #include "routine.h" -#ifdef __cplusplus -extern "C" { -#endif +#include +#include +#include -const struct routine *routine_from_name(const char *); -void routine_get_all(const struct routine ***, unsigned *); - -#ifdef __cplusplus -} -#endif +[[nodiscard]] std::optional routine_from_name(std::string_view name); +[[nodiscard]] std::pair routine_get_all(); #endif /* ROUTINES_H */ diff --git a/src/sortstring.c b/src/sortstring.cpp similarity index 62% rename from src/sortstring.c rename to src/sortstring.cpp index 6b5710b..87409ca 100644 --- a/src/sortstring.c +++ b/src/sortstring.cpp @@ -20,8 +20,6 @@ * IN THE SOFTWARE. */ -#define _GNU_SOURCE - #include "timing.h" #include "vmainfo.h" #include "routines.h" @@ -29,12 +27,13 @@ #include "util/debug.h" #include "util/sdt.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -42,192 +41,184 @@ #include #include #include +#include -static struct { - const struct routine *r; - char *write_filename; - unsigned suffixsorting : 1; - unsigned check_result : 1; - unsigned oprofile : 1; - unsigned write : 1; - unsigned xml_stats : 1; - unsigned hugetlb_text : 1; - unsigned hugetlb_pointers : 1; - unsigned text_raw : 1; - int perf_control_fd; +namespace { + +struct { + const routine *r = nullptr; + std::string write_filename; + bool suffixsorting = false; + bool check_result = false; + bool oprofile = false; + bool write = false; + bool xml_stats = false; + bool hugetlb_text = false; + bool hugetlb_pointers = false; + bool text_raw = false; + int perf_control_fd = 0; } opts; -static FILE *log_file; +FILE *log_file; -static void +void open_log_file(void) { - char *log_fn = NULL; - char *host = NULL; if (log_file) return; - host = getenv("HOSTNAME"); - if (host && *host) - if (asprintf(&log_fn, "sortstring_log_%s", host) == -1) - log_fn = NULL; - if (log_fn) - log_file = fopen(log_fn, "a"); - else - log_file = fopen("sortstring_log", "a"); - free(log_fn); + std::string log_fn = "sortstring_log"; + if (const char *host = std::getenv("HOSTNAME"); host && *host) { + log_fn += '_'; + log_fn += host; + } + log_file = std::fopen(log_fn.c_str(), "a"); if (log_file) setlinebuf(log_file); } -static void +void perf_control_write(int fd, const char *msg) { - ssize_t len = (ssize_t)strlen(msg); + ssize_t len = (ssize_t)std::strlen(msg); ssize_t ret = write(fd, msg, len); if (ret != len) { int err = errno; - fprintf(stderr, + std::fprintf(stderr, "ERROR: perf control fd write %s failed (ret=%zd, errno=%d): %s\n", - msg, ret, err, strerror(err)); + msg, ret, err, std::strerror(err)); if (log_file) - fprintf(log_file, + std::fprintf(log_file, "FATAL: perf control fd write %s failed (ret=%zd, errno=%d): %s\n", - msg, ret, err, strerror(err)); - exit(1); + msg, ret, err, std::strerror(err)); + std::exit(1); } } -static void +void perf_control_enable(int fd) { perf_control_write(fd, "enable\n"); } -static void +void perf_control_disable(int fd) { perf_control_write(fd, "disable\n"); } -static void +void opcontrol_start(void) { - int ret = system("opcontrol --start"); + int ret = std::system("opcontrol --start"); if (ret == -1 || WIFEXITED(ret) == 0 || WEXITSTATUS(ret) != 0) { - fprintf(stderr, "ERROR: opcontrol --start failed.\n"); + std::fprintf(stderr, "ERROR: opcontrol --start failed.\n"); if (log_file) - fprintf(log_file, + std::fprintf(log_file, "FATAL: opcontrol --start failed. " "ret=%d, WIFEXITED=%d, WEXITSTATUS=%d\n", ret, WIFEXITED(ret), WEXITSTATUS(ret)); - exit(1); + std::exit(1); } } -static void +void opcontrol_stop(void) { - int ret = system("opcontrol --stop"); + int ret = std::system("opcontrol --stop"); if (ret == -1 || WIFEXITED(ret) == 0 || WEXITSTATUS(ret) != 0) { - fprintf(stderr, "ERROR: opcontrol --stop failed."); + std::fprintf(stderr, "ERROR: opcontrol --stop failed."); if (log_file) - fprintf(log_file, + std::fprintf(log_file, "FATAL: opcontrol --stop failed. " "ret=%d, WIFEXITED=%d, WEXITSTATUS=%d\n", ret, WIFEXITED(ret), WEXITSTATUS(ret)); - exit(1); + std::exit(1); } } -static char * -bazename(const char *fname) +std::string +bazename(std::string_view fname) { - static char buf[300]; - strcpy(buf, fname); - return basename(buf); + std::string copy(fname); + return basename(copy.data()); } -static void * +[[nodiscard]] void * alloc_bytes(size_t bytes, int hugetlb) { - int map_flags; - void *p; - map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; if (hugetlb) map_flags |= MAP_HUGETLB; - p = mmap(NULL, bytes, PROT_READ | PROT_WRITE, map_flags, -1, 0); + void *p = mmap(nullptr, bytes, PROT_READ | PROT_WRITE, map_flags, -1, 0); if (p == MAP_FAILED) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to mmap memory for input: %s.\n", - strerror(errno)); - exit(1); + std::strerror(errno)); + std::exit(1); } return p; } -static unsigned char * +[[nodiscard]] unsigned char * alloc_text(size_t bytes) { - void *p = alloc_bytes(bytes, opts.hugetlb_text); - return (unsigned char *)p; + return static_cast(alloc_bytes(bytes, opts.hugetlb_text)); } -static unsigned char ** +[[nodiscard]] unsigned char ** alloc_pointers(size_t num) { - void *p = alloc_bytes(num*sizeof(unsigned char *), - opts.hugetlb_pointers); - return (unsigned char **)p; + return static_cast( + alloc_bytes(num * sizeof(unsigned char *), opts.hugetlb_pointers)); } -static void +void free_text(unsigned char *text, size_t text_len) { - munmap((void *)text, text_len); + munmap(text, text_len); } -static void +void free_pointers(unsigned char **strings, size_t strings_len) { - munmap((void *)strings, strings_len); + munmap(strings, strings_len); } -static off_t +[[nodiscard]] off_t file_size(int fd) { - off_t size; - size = lseek(fd, 0, SEEK_END); + off_t size = lseek(fd, 0, SEEK_END); if (size == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to lseek() input file: %s.\n", - strerror(errno)); - exit(1); + std::strerror(errno)); + std::exit(1); } if (lseek(fd, 0, SEEK_SET) == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to lseek() input file: %s.\n", - strerror(errno)); - exit(1); + std::strerror(errno)); + std::exit(1); } return size; } -static void +void input_copy(const char *fname, unsigned char **text_, size_t *text_len_) { int fd = open(fname, O_RDONLY); if (fd == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to open() input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } off_t filesize = file_size(fd); if (filesize <= 0) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: input file '%s' empty.\n", fname); - exit(1); + std::exit(1); } unsigned char *text = alloc_text(filesize); const size_t block_size = 128*1024; @@ -239,23 +230,23 @@ input_copy(const char *fname, unsigned char **text_, size_t *text_len_) if (ret < 0) { if (errno == EINTR) continue; - fprintf(stderr, "ERROR: failed read() " + std::fprintf(stderr, "ERROR: failed read() " "from input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } else if (ret == 0) { - fprintf(stderr, "ERROR: EOF read() before reading " + std::fprintf(stderr, "ERROR: EOF read() before reading " "whole input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } i += ret; } if (close(fd) == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to close() input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } *text_ = text; *text_len_ = filesize; @@ -263,41 +254,41 @@ input_copy(const char *fname, unsigned char **text_, size_t *text_len_) /* mmap() input data that is in raw format (uses NULL bytes for delimiting * strings). */ -static void +void input_mmap(const char *fname, unsigned char **text, size_t *text_len) { int fd = open(fname, O_RDONLY); if (fd == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to open() input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } off_t filesize = file_size(fd); if (filesize <= 0) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: input file '%s' empty.\n", fname); - exit(1); + std::exit(1); } void *raw = mmap(0, filesize, PROT_READ, MAP_PRIVATE, fd, 0); if (raw == MAP_FAILED) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to mmap input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } if (close(fd) == -1) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to close() input file '%s': %s.\n", - fname, strerror(errno)); - exit(1); + fname, std::strerror(errno)); + std::exit(1); } - *text = (unsigned char *)raw; + *text = static_cast(raw); *text_len = filesize; } -static void +void readbytes(const char *fname, unsigned char **text, size_t *text_len) { /* mapping file with MAP_HUGETLB does not work. */ @@ -307,7 +298,7 @@ readbytes(const char *fname, unsigned char **text, size_t *text_len) return input_copy(fname, text, text_len); } -static void +void create_strings_delim(unsigned char *text, size_t text_len, int delim, unsigned char ***strings, size_t *strings_cnt) { @@ -316,10 +307,10 @@ create_strings_delim(unsigned char *text, size_t text_len, int delim, if (text[i] == delim) ++strs_cnt; if (strs_cnt == 0) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: unable to read any lines from the input " "file.\n"); - exit(1); + std::exit(1); } unsigned char **strs = alloc_pointers(strs_cnt); unsigned char *line_start = text; @@ -334,7 +325,7 @@ create_strings_delim(unsigned char *text, size_t text_len, int delim, *strings_cnt = strs_cnt; } -static void +void create_strings(unsigned char *text, size_t text_len, unsigned char ***strings, size_t *strings_cnt) { @@ -346,7 +337,7 @@ create_strings(unsigned char *text, size_t text_len, strings, strings_cnt); } -static void +void create_suffixes(unsigned char *text, size_t text_len, unsigned char ***strings, size_t *strings_cnt) { @@ -357,40 +348,34 @@ create_suffixes(unsigned char *text, size_t text_len, *strings_cnt = text_len; } -static void +void write_result(unsigned char **strings, size_t n) { - FILE *fp; - if (!opts.write_filename) { - const char *username = getenv("USERNAME"); + if (opts.write_filename.empty()) { + const char *username = std::getenv("USERNAME"); if (!username) username = ""; - if (asprintf(&opts.write_filename, "/tmp/%s/alg.out", username) == -1) - opts.write_filename = NULL; + opts.write_filename = "/tmp/"; + opts.write_filename += username; + opts.write_filename += "/alg.out"; } - fp = fopen(opts.write_filename, "w"); + FILE *fp = std::fopen(opts.write_filename.c_str(), "w"); if (!fp) { - fprintf(stderr, + std::fprintf(stderr, "WARNING: --write failed: " "unable to open file for writing!\n"); return; } for (size_t i=0; i < n; ++i) { - fputs((const char *)strings[i], fp); - fputc('\n', fp); + std::fputs(reinterpret_cast(strings[i]), fp); + std::fputc('\n', fp); } - fclose(fp); - fprintf(stderr, "Wrote sorted output to '%s'.\n", - opts.write_filename); - /* - std::cout << "\n"; - system("md5sum " + opts.write_filename); - system("cat " + opts.input_filename + ".md5sum"); - std::cout << "\n"; - */ + std::fclose(fp); + std::fprintf(stderr, "Wrote sorted output to '%s'.\n", + opts.write_filename.c_str()); } -static void +void print_timing_results_xml(void) { /* @@ -402,17 +387,17 @@ print_timing_results_xml(void) */ } -static void +void print_timing_results_human(void) { - printf("%10.2f ms : wall-clock\n", gettime_wall_clock()); - printf("%10.2f ms : user\n", gettime_user()); - printf("%10.2f ms : sys\n", gettime_sys()); - printf("%10.2f ms : user+sys\n", gettime_user_sys()); - printf("%10.2f ms : PROCESS_CPUTIME\n", gettime_process_cputime()); + std::printf("%10.2f ms : wall-clock\n", gettime_wall_clock()); + std::printf("%10.2f ms : user\n", gettime_user()); + std::printf("%10.2f ms : sys\n", gettime_sys()); + std::printf("%10.2f ms : user+sys\n", gettime_user_sys()); + std::printf("%10.2f ms : PROCESS_CPUTIME\n", gettime_process_cputime()); } -static void +void print_timing_results(void) { if (opts.xml_stats) @@ -422,10 +407,10 @@ print_timing_results(void) } int -run(const struct routine *r, unsigned char **strings, size_t n) +run(const routine *r, unsigned char **strings, size_t n) { int ret = 0; - puts("Timing ..."); + std::puts("Timing ..."); if (opts.oprofile) opcontrol_start(); if (opts.perf_control_fd > 0) @@ -443,128 +428,129 @@ run(const struct routine *r, unsigned char **strings, size_t n) if (opts.check_result) { ret = check_result(strings, n); if (ret == 0) - fprintf(stderr, "Check: GOOD\n"); + std::fprintf(stderr, "Check: GOOD\n"); } if (opts.write) write_result(strings, n); return ret; } -static void +void print_alg_names_and_descs(void) { - const struct routine **routines; - unsigned i = 0, routines_cnt; - routine_get_all(&routines, &routines_cnt); + auto [routines, routines_cnt] = routine_get_all(); + unsigned i = 0; if (routines[0]->multicore == 0) { - puts(":: SINGLE CORE ROUTINES ::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); - puts(":: NAME :::::::::::::::::::::: DESCRIPTION :::::::::::::::::::::::::::::::::::::"); + std::puts(":: SINGLE CORE ROUTINES ::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + std::puts(":: NAME :::::::::::::::::::::: DESCRIPTION :::::::::::::::::::::::::::::::::::::"); for (i=0; i < routines_cnt && routines[i]->multicore == 0; ++i) - if (strlen(routines[i]->name) > 30) { - printf("%s\n", routines[i]->name); - printf("%30s %s\n", "", routines[i]->desc); + if (std::strlen(routines[i]->name) > 30) { + std::printf("%s\n", routines[i]->name); + std::printf("%30s %s\n", "", routines[i]->desc); } else - printf("%-30s %s\n", routines[i]->name, routines[i]->desc); + std::printf("%-30s %s\n", routines[i]->name, routines[i]->desc); } if (i < routines_cnt && routines[i]->multicore) { if (i) - puts(""); - puts(":: MULTI CORE ROUTINES :::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); - puts(":: NAME :::::::::::::::::::::: DESCRIPTION :::::::::::::::::::::::::::::::::::::"); + std::puts(""); + std::puts(":: MULTI CORE ROUTINES :::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + std::puts(":: NAME :::::::::::::::::::::: DESCRIPTION :::::::::::::::::::::::::::::::::::::"); for (; i < routines_cnt; ++i) - if (strlen(routines[i]->name) > 30) { - printf("%s\n", routines[i]->name); - printf("%30s %s\n", "", routines[i]->desc); + if (std::strlen(routines[i]->name) > 30) { + std::printf("%s\n", routines[i]->name); + std::printf("%30s %s\n", "", routines[i]->desc); } else - printf("%-30s %s\n", routines[i]->name, routines[i]->desc); + std::printf("%-30s %s\n", routines[i]->name, routines[i]->desc); } } -static void +void print_alg_names(void) { - const struct routine **routines; - unsigned i, routines_cnt; - routine_get_all(&routines, &routines_cnt); - for (i=0; i < routines_cnt; ++i) - puts(routines[i]->name); + auto [routines, routines_cnt] = routine_get_all(); + for (unsigned i=0; i < routines_cnt; ++i) + std::puts(routines[i]->name); } -static void -routine_information(const struct routine *r) +void +routine_information(const routine *r) { - printf("Routine (%s): %s\n", + std::printf("Routine (%s): %s\n", r->multicore ? "multi core" : "single core", r->name); - printf(" \"%s\"\n", r->desc); - printf("\n"); + std::printf(" \"%s\"\n", r->desc); + std::printf("\n"); } -static void +void input_information(unsigned char *text, size_t text_len, unsigned char **strings, size_t strings_len) { size_t input_mb = text_len / (1024*1024); size_t input_kb = text_len / 1024; if (input_mb) - printf(" size: %zu MB (%zu kB, %zu bytes)\n", + std::printf(" size: %zu MB (%zu kB, %zu bytes)\n", input_mb, input_kb, text_len); else if (input_kb) - printf(" size: %zu kB (%zu bytes)\n", + std::printf(" size: %zu kB (%zu bytes)\n", input_kb, text_len); else - printf(" size: %zu bytes\n", text_len); - printf(" strings: %zu\n", strings_len); - puts(""); - char *vma_info_text = vma_info(text); - char *vma_info_strings = vma_info(strings); - if (strcmp(vma_info_text, vma_info_strings) == 0) { - puts("VMA information for text and string pointer arrays:"); - puts(vma_info_text); + std::printf(" size: %zu bytes\n", text_len); + std::printf(" strings: %zu\n", strings_len); + std::puts(""); + std::string vma_info_text = vma_info(text); + std::string vma_info_strings = vma_info(strings); + if (vma_info_text == vma_info_strings) { + std::puts("VMA information for text and string pointer arrays:"); + std::puts(vma_info_text.c_str()); } else { - puts("VMA information for text array:"); - puts(vma_info_text); - puts("VMA information for string pointer array:"); - puts(vma_info_strings); + std::puts("VMA information for text array:"); + std::puts(vma_info_text.c_str()); + std::puts("VMA information for string pointer array:"); + std::puts(vma_info_strings.c_str()); } - free(vma_info_text); - free(vma_info_strings); } -static void +void cpu_information(void) { - int i; - int maxcpu = -1; - size_t cpus_setsize = 0; - char *cpus_al = cpus_allowed_list(); - cpu_set_t *cpus = cpus_allowed(&cpus_setsize, &maxcpu); - if (!cpus_al && !cpus) + std::string cpus_al = cpus_allowed_list(); + auto [cpus, cpus_setsize, maxcpu] = cpus_allowed(); + if (cpus_al.empty() && !cpus) return; - printf("CPU information:\n"); - if (cpus_al) - printf(" CPUs allowed: %s\n", cpus_al); - for (i=0; i < maxcpu; ++i) { - if (CPU_ISSET_S(i, cpus_setsize, cpus)) { - int min_freq, max_freq; - printf(" CPU%d", i); - min_freq = cpu_scaling_min_freq(i); - max_freq = cpu_scaling_max_freq(i); - if (min_freq != -1 && max_freq != -1) - printf(", scaling frequencies: [%dMHz .. %dMHz]", - max_freq/1000, min_freq/1000); - puts(""); + std::printf("CPU information:\n"); + if (!cpus_al.empty()) + std::printf(" CPUs allowed: %s\n", cpus_al.c_str()); + if (auto boost = cpu_boost_enabled()) + std::printf(" Turbo/boost: %s\n", *boost ? "on" : "off"); + for (int i=0; i < maxcpu; ++i) { + if (!CPU_ISSET_S(i, cpus_setsize, cpus)) + continue; + const cpu_info info = cpu_info_for(i); + std::printf(" CPU%d", i); + switch (info.klass) { + case cpu_info::core_class::performance: std::printf(" (P-core)"); break; + case cpu_info::core_class::efficiency: std::printf(" (E-core)"); break; + case cpu_info::core_class::unknown: break; } + if (info.scaling_min_khz && info.scaling_max_khz) + std::printf(", scaling [%dMHz .. %dMHz]", + *info.scaling_min_khz/1000, + *info.scaling_max_khz/1000); + if (info.hw_max_khz && info.scaling_max_khz && + *info.hw_max_khz > *info.scaling_max_khz) + std::printf(", hw peak %dMHz", *info.hw_max_khz/1000); + std::puts(""); } - putchar('\n'); - free(cpus_al); - free(cpus); + std::putchar('\n'); + if (cpus) + CPU_FREE(cpus); } -static void +void usage(void) { - puts( + std::puts( "String sorting\n" "--------------\n" "\n" @@ -614,18 +600,19 @@ usage(void) "\n"); } -static void +void print_cmdline(int argc, char **argv, FILE *fp) { - int i; if (!fp) return; - fprintf(fp, "Command line:"); - for (i=0; i < argc; ++i) - fprintf(fp, " %s", argv[i]); - fprintf(fp, "\n"); + std::fprintf(fp, "Command line:"); + for (int i=0; i < argc; ++i) + std::fprintf(fp, " %s", argv[i]); + std::fprintf(fp, "\n"); } +} // namespace + int main(int argc, char **argv) { int ret = 0; @@ -663,33 +650,33 @@ int main(int argc, char **argv) print_alg_names(); return 0; case 1003: - opts.check_result = 1; + opts.check_result = true; break; case 1004: - opts.suffixsorting = 1; + opts.suffixsorting = true; break; case 1005: - opts.write = 1; + opts.write = true; if (optarg) opts.write_filename = optarg; break; case 1007: - opts.oprofile = 1; + opts.oprofile = true; break; case 1008: - opts.xml_stats = 1; + opts.xml_stats = true; break; case 1009: - opts.hugetlb_text = 1; + opts.hugetlb_text = true; break; case 1010: - opts.hugetlb_pointers = 1; + opts.hugetlb_pointers = true; break; case 1011: - opts.text_raw = 1; + opts.text_raw = true; break; case 1012: - opts.perf_control_fd = atoi(optarg); + opts.perf_control_fd = std::atoi(optarg); break; case '?': default: @@ -697,50 +684,51 @@ int main(int argc, char **argv) } } if (argc - 2 != optind) { - fprintf(stderr, + std::fprintf(stderr, "ERROR: wrong number of arguments.\n"); return 1; } const char *algorithm = argv[optind]; - if (!algorithm || strlen(algorithm) == 0) { - fprintf(stderr, + if (!algorithm || std::strlen(algorithm) == 0) { + std::fprintf(stderr, "ERROR: please specify algorithm name.\n"); return 1; } - opts.r = routine_from_name(algorithm); - if (!opts.r) { - fprintf(stderr, + if (auto r = routine_from_name(algorithm); r) { + opts.r = *r; + } else { + std::fprintf(stderr, "ERROR: no match found for algorithm '%s'!\n", algorithm); return 1; } const char *filename = argv[optind+1]; - if (!filename || strlen(filename) == 0) { - fprintf(stderr, + if (!filename || std::strlen(filename) == 0) { + std::fprintf(stderr, "ERROR: please specify input filename.\n"); return 1; } open_log_file(); if (log_file) - fprintf(log_file, "===START===\n"); + std::fprintf(log_file, "===START===\n"); print_cmdline(argc, argv, log_file); routine_information(opts.r); cpu_information(); - unsigned long seed = getpid()*time(0); + unsigned long seed = getpid() * std::time(nullptr); //seed = 0xdeadbeef; srand48(seed); if (log_file) - fprintf(log_file, "Random seed: %lu.\n", seed); - printf("Input (%s): %s ...\n", + std::fprintf(log_file, "Random seed: %lu.\n", seed); + std::printf("Input (%s): %s ...\n", opts.text_raw ? "RAW" : "plain", - bazename(filename)); + bazename(filename).c_str()); unsigned char *text; unsigned char **strings; size_t text_len, strings_len; readbytes(filename, &text, &text_len); if (opts.suffixsorting) { if (log_file) - fprintf(log_file, "Suffix sorting mode!\n"); + std::fprintf(log_file, "Suffix sorting mode!\n"); create_suffixes(text, text_len, &strings, &strings_len); } else { create_strings(text, text_len, &strings, &strings_len); @@ -750,8 +738,8 @@ int main(int argc, char **argv) free_text(text, text_len); free_pointers(strings, strings_len); if (log_file) { - fprintf(log_file, "===DONE===\n"); - fclose(log_file); + std::fprintf(log_file, "===DONE===\n"); + std::fclose(log_file); } return ret; } diff --git a/src/util/cpus_allowed.c b/src/util/cpus_allowed.c deleted file mode 100644 index b17cee5..0000000 --- a/src/util/cpus_allowed.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2012 by Tommi Rantala - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "cpus_allowed.h" - -#include -#include -#include - -static char * -status_entry(const char *key) -{ - char *result = NULL; - char *line = NULL; - size_t line_n = 0; - FILE *fp; - fp = fopen("/proc/self/status", "r"); - if (!fp) - goto done; - while (getline(&line, &line_n, fp) != -1) { - char *v; - v = strchr(line, ':'); - if (!v || *v == '\0') - continue; - *v = '\0'; - if (strcmp(line, key) != 0) - continue; - ++v; - while (*v == ' ' || *v == '\t') - ++v; - if (strlen(v) > 1) - v[strlen(v)-1] = '\0'; - if (*v == '\0') - goto done; - result = line; - while ((*line++ = *v++)) - ; - goto done; - } -done: - if (!result) - free(line); - if (fp) - fclose(fp); - return result; -} - -char * -cpus_allowed_list(void) -{ - return status_entry("Cpus_allowed_list"); -} - -static int -ishexdigit(char ch) -{ - return (ch >= '0' && ch <= '9') - || (ch >= 'a' && ch <= 'f'); -} - -static int -hex2int(char ch) -{ - if (ch >= '0' && ch <= '9') - return ch - '0'; - if (ch >= 'a' && ch <= 'f') - return ch - 'a' + 10; - abort(); - return 0; -} - -static int -high_bit_order(char *allowed) -{ - int order = -1; - int i = 0; - int k = strlen(allowed)-1; - for (; k >= 0; --k) { - char ch = allowed[k]; - if (!ishexdigit(ch)) - continue; - int mask = hex2int(ch); - if (mask) { - int neworder; - if (mask & 8) - neworder = 4 + i; - else if (mask & 4) - neworder = 3 + i; - else if (mask & 2) - neworder = 2 + i; - else - neworder = 1 + i; - if (neworder > order) - order = neworder; - } - i += 4; - } - return order; -} - -static void -set_cpu_bits(char *allowed, cpu_set_t *c, size_t setsize) -{ - int i = 0; - int k = strlen(allowed)-1; - for (; k >= 0; --k) { - char ch = allowed[k]; - if (!ishexdigit(ch)) - continue; - int mask = hex2int(ch); - if (mask & 1) CPU_SET_S(i+0, setsize, c); - if (mask & 2) CPU_SET_S(i+1, setsize, c); - if (mask & 4) CPU_SET_S(i+2, setsize, c); - if (mask & 8) CPU_SET_S(i+3, setsize, c); - i += 4; - } -} - -cpu_set_t * -cpus_allowed(size_t *setsize, int *maxcpu) -{ - cpu_set_t *c = NULL; - char *allowed = status_entry("Cpus_allowed"); - if (!allowed || strlen(allowed) == 0) - goto done; - *maxcpu = high_bit_order(allowed); - if (*maxcpu == -1) - goto done; - c = CPU_ALLOC(*maxcpu+1); - if (!c) - goto done; - *setsize = CPU_ALLOC_SIZE(*maxcpu+1); - set_cpu_bits(allowed, c, *setsize); -done: - free(allowed); - return c; -} - -int -cpu_scaling_min_freq(int cpu) -{ - int min_freq; - FILE *fp; - char *filename = NULL; - if (asprintf(&filename, - "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", - cpu) == -1) { - return -1; - } - fp = fopen(filename, "r"); - free(filename); - if (!fp) - return -1; - if (fscanf(fp, "%d", &min_freq) != 1) - min_freq = -1; - fclose(fp); - return min_freq; -} - -int -cpu_scaling_max_freq(int cpu) -{ - int max_freq; - FILE *fp; - char *filename = NULL; - if (asprintf(&filename, - "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", - cpu) == -1) { - return -1; - } - fp = fopen(filename, "r"); - free(filename); - if (!fp) - return -1; - if (fscanf(fp, "%d", &max_freq) != 1) - max_freq = -1; - fclose(fp); - return max_freq; -} diff --git a/src/util/cpus_allowed.cpp b/src/util/cpus_allowed.cpp new file mode 100644 index 0000000..726891a --- /dev/null +++ b/src/util/cpus_allowed.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2012 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "cpus_allowed.h" + +#include +#include +#include +#include +#include + +namespace { + +/* Read /proc/self/status, return the (whitespace-trimmed) value of the + * requested key, or an empty string if not present. */ +std::string +status_entry(std::string_view key) +{ + std::ifstream f("/proc/self/status"); + if (!f) return std::string(); + for (std::string line; std::getline(f, line); ) { + auto colon = line.find(':'); + if (colon == std::string::npos) continue; + if (std::string_view(line).substr(0, colon) != key) continue; + auto v = colon + 1; + while (v < line.size() && (line[v] == ' ' || line[v] == '\t')) + ++v; + auto end = line.size(); + while (end > v && std::isspace(static_cast(line[end-1]))) + --end; + return line.substr(v, end - v); + } + return std::string(); +} + +bool +ishex(char ch) +{ + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f'); +} + +int +hex_val(char ch) +{ + return (ch >= '0' && ch <= '9') ? ch - '0' : ch - 'a' + 10; +} + +/* Highest bit index set in the hex bitmask string `allowed` (low-order + * digits on the right). Non-hex characters (e.g. comma separators) are + * skipped. Returns -1 if no bits are set. */ +int +high_bit_order(std::string_view allowed) +{ + int order = -1; + int i = 0; + for (auto it = allowed.rbegin(); it != allowed.rend(); ++it) { + char ch = *it; + if (!ishex(ch)) continue; + int mask = hex_val(ch); + if (mask) { + int neworder; + if (mask & 8) neworder = 4 + i; + else if (mask & 4) neworder = 3 + i; + else if (mask & 2) neworder = 2 + i; + else neworder = 1 + i; + if (neworder > order) order = neworder; + } + i += 4; + } + return order; +} + +void +set_cpu_bits(std::string_view allowed, cpu_set_t *c, size_t setsize) +{ + int i = 0; + for (auto it = allowed.rbegin(); it != allowed.rend(); ++it) { + char ch = *it; + if (!ishex(ch)) continue; + int mask = hex_val(ch); + if (mask & 1) CPU_SET_S(i+0, setsize, c); + if (mask & 2) CPU_SET_S(i+1, setsize, c); + if (mask & 4) CPU_SET_S(i+2, setsize, c); + if (mask & 8) CPU_SET_S(i+3, setsize, c); + i += 4; + } +} + +std::optional +read_int_file(const std::string &path) +{ + std::ifstream f(path); + if (!f) return std::nullopt; + int v; + if (!(f >> v)) return std::nullopt; + return v; +} + +/* Parse a Linux cpulist string ("0-3,5,7-9") and report whether `cpu` + * is a member. Returns std::nullopt if the file cannot be read; false + * if the file is readable but `cpu` is not listed. */ +std::optional +cpu_in_cpulist_file(int cpu, const std::string &path) +{ + std::ifstream f(path); + if (!f) return std::nullopt; + std::string list; + std::getline(f, list); + std::stringstream ss(list); + std::string tok; + while (std::getline(ss, tok, ',')) { + auto dash = tok.find('-'); + int lo, hi; + try { + if (dash == std::string::npos) { + lo = hi = std::stoi(tok); + } else { + lo = std::stoi(tok.substr(0, dash)); + hi = std::stoi(tok.substr(dash + 1)); + } + } catch (...) { + continue; + } + if (cpu >= lo && cpu <= hi) return true; + } + return false; +} + +} // namespace + +std::string +cpus_allowed_list() +{ + return status_entry("Cpus_allowed_list"); +} + +cpus_info +cpus_allowed() +{ + cpus_info info; + std::string allowed = status_entry("Cpus_allowed"); + if (allowed.empty()) return info; + int top = high_bit_order(allowed); + if (top == -1) return info; + cpu_set_t *c = CPU_ALLOC(top + 1); + if (!c) return info; + info.set = c; + info.setsize = CPU_ALLOC_SIZE(top + 1); + info.maxcpu = top; + CPU_ZERO_S(info.setsize, info.set); + set_cpu_bits(allowed, info.set, info.setsize); + return info; +} + +cpu_info +cpu_info_for(int cpu) +{ + std::ostringstream base; + base << "/sys/devices/system/cpu/cpu" << cpu << "/cpufreq/"; + const std::string b = base.str(); + + cpu_info info; + info.scaling_min_khz = read_int_file(b + "scaling_min_freq"); + info.scaling_max_khz = read_int_file(b + "scaling_max_freq"); + /* Hardware peak (boost-capable) frequency: + * - AMD pstate: amd_pstate_max_freq is the per-core boost cap; + * cpuinfo_max_freq tracks scaling_max_freq when boost is off. + * - Intel: cpuinfo_max_freq is the turbo cap. */ + info.hw_max_khz = read_int_file(b + "amd_pstate_max_freq"); + if (!info.hw_max_khz) + info.hw_max_khz = read_int_file(b + "cpuinfo_max_freq"); + /* Intel hybrid topology: cpu_core PMU lists P-cores, cpu_atom E-cores. */ + if (cpu_in_cpulist_file(cpu, "/sys/devices/cpu_core/cpus").value_or(false)) + info.klass = cpu_info::core_class::performance; + else if (cpu_in_cpulist_file(cpu, "/sys/devices/cpu_atom/cpus").value_or(false)) + info.klass = cpu_info::core_class::efficiency; + return info; +} + +/* Turbo/boost state, reported as the inverse-sense pair the kernel + * exposes for each driver family. */ +std::optional +cpu_boost_enabled() +{ + if (auto v = read_int_file("/sys/devices/system/cpu/cpufreq/boost")) + return *v != 0; + if (auto v = read_int_file("/sys/devices/system/cpu/intel_pstate/no_turbo")) + return *v == 0; + return std::nullopt; +} diff --git a/src/util/cpus_allowed.h b/src/util/cpus_allowed.h index ed6fc9e..5e3ca60 100644 --- a/src/util/cpus_allowed.h +++ b/src/util/cpus_allowed.h @@ -23,12 +23,33 @@ #ifndef CPUS_ALLOWED_H #define CPUS_ALLOWED_H -#define _GNU_SOURCE #include -char *cpus_allowed_list(void); -cpu_set_t *cpus_allowed(size_t *, int *maxcpu); -int cpu_scaling_max_freq(int cpu); -int cpu_scaling_min_freq(int cpu); +#include +#include +#include + +struct cpus_info { + cpu_set_t *set = nullptr; + size_t setsize = 0; + int maxcpu = 0; +}; + +struct cpu_info { + enum class core_class { + unknown, + performance, // Intel P-core (cpu_core PMU) + efficiency, // Intel E-core (cpu_atom PMU) + }; + std::optional scaling_min_khz; + std::optional scaling_max_khz; + std::optional hw_max_khz; // amd_pstate_max_freq or cpuinfo_max_freq + core_class klass = core_class::unknown; +}; + +[[nodiscard]] std::string cpus_allowed_list(); +[[nodiscard]] cpus_info cpus_allowed(); +[[nodiscard]] cpu_info cpu_info_for(int cpu); +[[nodiscard]] std::optional cpu_boost_enabled(); #endif /* CPUS_ALLOWED_H */ diff --git a/src/util/debug.h b/src/util/debug.h index 6b3b1eb..74f6ad1 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -38,9 +38,10 @@ static std::string __debug_indent_str; #endif #endif /* __cplusplus */ -static inline int +[[nodiscard]] static inline int check_result(unsigned char **strings, size_t n) { + if (n < 2) return 0; size_t wrong = 0; size_t identical = 0; size_t invalid = 0; diff --git a/src/util/median.h b/src/util/median.h index f4018da..bc70d15 100644 --- a/src/util/median.h +++ b/src/util/median.h @@ -24,21 +24,13 @@ #define UTIL_H #include "get_char.h" +#include template CharT med3char(CharT a, CharT b, CharT c) { - if (a == b) return a; - if (c == a || c == b) return c; - if (a < b) { - if (b < c) return b; - if (a < c) return c; - return a; - } - if (b > c) return b; - if (a < c) return a; - return c; + return std::max(std::min(a, b), std::min(std::max(a, b), c)); } template diff --git a/src/util/timing.c b/src/util/timing.c deleted file mode 100644 index f072214..0000000 --- a/src/util/timing.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2007-2008,2011 by Tommi Rantala - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -static struct timespec process_cputime_start; -static struct timespec process_cputime_stop; -static struct timespec monotonic_start; -static struct timespec monotonic_stop; -static struct rusage startclock; -static struct rusage stopclock; - -void timing_start(void) -{ - getrusage(RUSAGE_SELF, &startclock); - clock_gettime(CLOCK_MONOTONIC, &monotonic_start); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_cputime_start); -} - -void timing_stop(void) -{ - getrusage(RUSAGE_SELF, &stopclock); - clock_gettime(CLOCK_MONOTONIC, &monotonic_stop); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_cputime_stop); -} - -double gettime_wall_clock(void) -{ - double msecs_1 = monotonic_start.tv_nsec/1000000 + 1000*monotonic_start.tv_sec; - double msecs_2 = monotonic_stop.tv_nsec/1000000 + 1000*monotonic_stop.tv_sec; - return msecs_2 - msecs_1; -} - -double gettime_user(void) -{ - struct timeval result; - timersub(&stopclock.ru_utime, &startclock.ru_utime, &result); - return (double)(result.tv_sec*1000)+(double)(result.tv_usec)/1e3; -} - -double gettime_sys(void) -{ - struct timeval result; - timersub(&stopclock.ru_stime, &startclock.ru_stime, &result); - return (double)(result.tv_sec*1000)+(double)(result.tv_usec)/1e3; -} - -double gettime_user_sys(void) -{ - struct timeval result_user; - struct timeval result_sys; - struct timeval result; - timersub(&stopclock.ru_utime, &startclock.ru_utime, &result_user); - timersub(&stopclock.ru_stime, &startclock.ru_stime, &result_sys); - timeradd(&result_user, &result_sys, &result); - return (double)(result.tv_sec*1000)+(double)(result.tv_usec)/1e3; -} - -double gettime_process_cputime(void) -{ - double msecs_1 = process_cputime_start.tv_nsec/1000000 + 1000*process_cputime_start.tv_sec; - double msecs_2 = process_cputime_stop.tv_nsec/1000000 + 1000*process_cputime_stop.tv_sec; - return msecs_2 - msecs_1; -} diff --git a/src/util/timing.cpp b/src/util/timing.cpp new file mode 100644 index 0000000..91c3bea --- /dev/null +++ b/src/util/timing.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2007-2008,2011 by Tommi Rantala + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "timing.h" + +#include +#include +#include + +namespace { + +timespec process_cputime_start; +timespec process_cputime_stop; +timespec monotonic_start; +timespec monotonic_stop; +rusage startclock; +rusage stopclock; + +double +ms_between(const timespec &a, const timespec &b) +{ + return (b.tv_nsec / 1000000.0 + 1000.0 * b.tv_sec) + - (a.tv_nsec / 1000000.0 + 1000.0 * a.tv_sec); +} + +double +ms_between(const timeval &a, const timeval &b) +{ + timeval d; + timersub(&b, &a, &d); + return d.tv_sec * 1000.0 + d.tv_usec / 1000.0; +} + +} // namespace + +void +timing_start(void) +{ + getrusage(RUSAGE_SELF, &startclock); + clock_gettime(CLOCK_MONOTONIC, &monotonic_start); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_cputime_start); +} + +void +timing_stop(void) +{ + getrusage(RUSAGE_SELF, &stopclock); + clock_gettime(CLOCK_MONOTONIC, &monotonic_stop); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_cputime_stop); +} + +double +gettime_wall_clock(void) +{ + return ms_between(monotonic_start, monotonic_stop); +} + +double +gettime_user(void) +{ + return ms_between(startclock.ru_utime, stopclock.ru_utime); +} + +double +gettime_sys(void) +{ + return ms_between(startclock.ru_stime, stopclock.ru_stime); +} + +double +gettime_user_sys(void) +{ + return gettime_user() + gettime_sys(); +} + +double +gettime_process_cputime(void) +{ + return ms_between(process_cputime_start, process_cputime_stop); +} diff --git a/src/util/vmainfo.c b/src/util/vmainfo.cpp similarity index 58% rename from src/util/vmainfo.c rename to src/util/vmainfo.cpp index 46887c3..d4ebb34 100644 --- a/src/util/vmainfo.c +++ b/src/util/vmainfo.cpp @@ -20,11 +20,14 @@ * IN THE SOFTWARE. */ -#define _GNU_SOURCE #include "vmainfo.h" -#include -#include -#include + +#include +#include +#include +#include +#include +#include /* Format the /proc/pid/smaps key-value pairs into two columns: * @@ -53,77 +56,58 @@ * Private_Clean: 0 kB | MMUPageSize: 4 kB * Private_Dirty: 390636 kB | Locked: 0 kB */ -static void -add_smaps(char *buf, char **pairs, unsigned pairs_cnt) + +namespace { + +struct Entry { + std::string header; + std::vector pairs; +}; + +Entry +find_entry(unsigned long target) { - unsigned i, j; - for (i=0, j=pairs_cnt/2; i < pairs_cnt/2; ++i, ++j) { - pairs[i][strlen(pairs[i])-1] = '\0'; - strcat(buf, " "); - strcat(buf, pairs[i]); - strcat(buf, " | "); - strcat(buf, pairs[j]); - } - if (j < pairs_cnt) { - strcat(buf, " "); - strcat(buf, pairs[j]); + Entry e; + std::ifstream f("/proc/self/smaps"); + if (!f) return e; + for (std::string line; std::getline(f, line); ) { + unsigned long a, b; + if (std::sscanf(line.c_str(), "%lx-%lx", &a, &b) != 2) + continue; + if (!(a <= target && target < b)) + continue; + e.header = std::move(line); + while (std::getline(f, line)) { + if (line.empty()) break; + if (line[0] < 'A' || line[0] > 'Z') break; + if (line.find(':') == std::string::npos) break; + e.pairs.push_back(std::move(line)); + } + break; } + return e; } -static void -free_pairs(char **pairs, unsigned pairs_cnt) +std::string +format(const Entry &e) { - unsigned i; - for (i=0; i < pairs_cnt; ++i) - free(pairs[i]); - free(pairs); + if (e.header.empty()) return std::string(); + std::ostringstream out; + out << " " << e.header << '\n'; + const size_t n = e.pairs.size(); + const size_t half = n / 2; + for (size_t i = 0, j = half; i < half; ++i, ++j) + out << " " << e.pairs[i] << " | " << e.pairs[j] << '\n'; + if (half * 2 < n) + out << " " << e.pairs.back() << '\n'; + return out.str(); } -char * -vma_info(void *ptr) +} // namespace + +std::string +vma_info(const void *ptr) { - FILE *fp = NULL; - char *buf = NULL; - char *line = NULL; - char **pairs = NULL, **tmp = NULL; - unsigned pairs_cnt = 0; - size_t line_n = 0; - buf = malloc(2048); - if (!buf) - goto done; - buf[0] = 0; - fp = fopen("/proc/self/smaps", "r"); - if (!fp) - goto done; - while (getline(&line, &line_n, fp) != -1) { - unsigned long a, b; - if (sscanf(line, "%lx-%lx", &a, &b) != 2) - continue; - if (a <= (unsigned long)ptr && (unsigned long)ptr < b) { - /* OK, found it! */ - strcat(buf, " "); - strcat(buf, line); - while (getline(&line, &line_n, fp) != -1) { - if (line[0] >= 'A' && line[0] <= 'Z' - && strchr(line, ':') != NULL) { - tmp = realloc(pairs, (pairs_cnt+1) * sizeof(char *)); - if (!tmp) - goto done; - pairs = tmp; - pairs[pairs_cnt++] = line; - line = NULL; - line_n = 0; - } else { - free(line); - goto done; - } - } - } - } -done: - add_smaps(buf, pairs, pairs_cnt); - free_pairs(pairs, pairs_cnt); - if (fp) - fclose(fp); - return buf; + auto target = reinterpret_cast(ptr); + return format(find_entry(target)); } diff --git a/src/util/vmainfo.h b/src/util/vmainfo.h index 1e6b5a3..4e52fe5 100644 --- a/src/util/vmainfo.h +++ b/src/util/vmainfo.h @@ -23,7 +23,8 @@ #ifndef VMAINFO_H #define VMAINFO_H -/* Release return value with free() when no longer needed. */ -char *vma_info(void *ptr); +#include + +[[nodiscard]] std::string vma_info(const void *ptr); #endif /* VMAINFO_H */ diff --git a/src/vector_bagwell.h b/src/vector_bagwell.h index e109ae0..3c28a35 100644 --- a/src/vector_bagwell.h +++ b/src/vector_bagwell.h @@ -59,6 +59,7 @@ struct vector_bagwell _left_in_block = Initial << _index_block.size(); _insertpos = static_cast( malloc(_left_in_block*sizeof(T))); + if (!_insertpos) abort(); _index_block.push_back(_insertpos); } *_insertpos++ = t; diff --git a/src/vector_block.h b/src/vector_block.h index 10483df..09939b6 100644 --- a/src/vector_block.h +++ b/src/vector_block.h @@ -45,6 +45,7 @@ struct vector_block { if (__builtin_expect(is_full(), false)) { _insertpos = static_cast(malloc(B*sizeof(T))); + if (!_insertpos) abort(); _index_block.push_back(_insertpos); _left_in_block = B; } diff --git a/src/vector_brodnik.h b/src/vector_brodnik.h index 5f88258..077f18c 100644 --- a/src/vector_brodnik.h +++ b/src/vector_brodnik.h @@ -91,6 +91,7 @@ struct vector_brodnik _left_in_superblock = _superblock_size; } _insertpos = static_cast(malloc(_block_size*sizeof(T))); + if (!_insertpos) abort(); _index_block.push_back(_insertpos); _left_in_block = _block_size; --_left_in_superblock; diff --git a/src/vector_malloc.h b/src/vector_malloc.h index b2e76f9..4313f9c 100644 --- a/src/vector_malloc.h +++ b/src/vector_malloc.h @@ -62,9 +62,11 @@ class vector_malloc if (_capacity == 0) { _capacity = InitialSize; _data = static_cast(malloc(_capacity*sizeof(T))); + if (!_data) abort(); } else { _capacity <<= 1; T* t = static_cast(malloc(_capacity*sizeof(T))); + if (!t) abort(); (void) memcpy(t, _data, _size*sizeof(T)); free(_data); _data = t; @@ -103,9 +105,11 @@ class vector_malloc_counter_clear if (_capacity == 0) { _capacity = InitialSize; _data = static_cast(malloc(_capacity*sizeof(T))); + if (!_data) abort(); } else { _capacity <<= 1; T* t = static_cast(malloc(_capacity*sizeof(T))); + if (!t) abort(); (void) memcpy(t, _data, _size*sizeof(T)); free(_data); _data = t; diff --git a/src/vector_realloc.h b/src/vector_realloc.h index 6542b2f..dd7e3cd 100644 --- a/src/vector_realloc.h +++ b/src/vector_realloc.h @@ -63,7 +63,9 @@ class vector_realloc if (_capacity == 0) { _capacity = InitialSize; } - _data = static_cast(realloc(_data, _capacity*sizeof(T))); + T* data = static_cast(realloc(_data, _capacity*sizeof(T))); + if (!data) abort(); + _data = data; } T* _data; size_t _size; @@ -99,7 +101,9 @@ class vector_realloc_counter_clear if (_capacity == 0) { _capacity = InitialSize; } - _data = static_cast(realloc(_data, _capacity*sizeof(T))); + T* data = static_cast(realloc(_data, _capacity*sizeof(T))); + if (!data) abort(); + _data = data; } T* _data; size_t _size; @@ -139,14 +143,19 @@ class vector_realloc_shrink_clear if (_capacity == 0) { _capacity = InitialSize; } - _data = static_cast(realloc(_data, _capacity*sizeof(T))); + T* data = static_cast(realloc(_data, _capacity*sizeof(T))); + if (!data) abort(); + _data = data; } void shrink() { if (_capacity > 0x80000) { - _capacity = _capacity / 2; - _data = static_cast( - realloc(_data, _capacity*sizeof(T))); + const size_t capacity = _capacity / 2; + T* data = static_cast( + realloc(_data, capacity*sizeof(T))); + if (!data) abort(); + _data = data; + _capacity = capacity; } } T* _data; diff --git a/unit-test/main.cpp b/unit-test/main.cpp index 4fa731d..abd3ed1 100644 --- a/unit-test/main.cpp +++ b/unit-test/main.cpp @@ -209,15 +209,13 @@ test_routines() { std::cerr<<__PRETTY_FUNCTION__<name << ']' << std::endl; - for (size_t k=1; k < 2000; k += 200) { + for (size_t k=0; k < 2000; k += 200) { std::vector input; for (size_t i=0; i < k; ++i) input.push_back(strdup("aaa")); @@ -231,7 +229,7 @@ test_routines() for (size_t i=0; i < n; ++i) free(input[i]); } - for (size_t k=1; k < 1000; k += 200) { + for (size_t k=0; k < 1000; k += 200) { std::vector input; for (size_t i=0; i < k; ++i) { input.push_back(strdup("bb")); @@ -252,7 +250,7 @@ test_routines() for (size_t i=0; i < n; ++i) free(input[i]); } - for (size_t k=1; k < 10000; k += 2000) { + for (size_t k=0; k < 10000; k += 2000) { std::vector input; for (size_t i=0; i < k; ++i) { char buf[10];