diff --git a/.gitignore b/.gitignore index fd3b9ca..82fc6c3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ cmake-build-debug test-example build .vscode +/.cproject +/.project +/.settings/ +/.gitignore diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec5c95..461343e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,97 +1,99 @@ -cmake_minimum_required(VERSION 3.1.0) -project(ExpansionHunter CXX) +cmake_minimum_required(VERSION 3.13) +project(ExpansionHunter) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) -enable_testing() -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Check for required packages: +find_package(ZLIB REQUIRED) +find_package(BZip2 REQUIRED) +find_package(LibLZMA REQUIRED) +find_package(CURL REQUIRED) +find_package(Git REQUIRED) include(ExternalProject) +set(installDir ${CMAKE_CURRENT_BINARY_DIR}/install) -######################### Google Test ############################ -# Download and unpack googletest at configure time -configure_file(cmake/google_test.cmake googletest-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) -if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") -endif() -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) -if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") + +ExternalProject_Add(htslib + BUILD_IN_SOURCE YES + GIT_REPOSITORY "https://github.com/samtools/htslib.git" + GIT_TAG "1.10.2" + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install prefix=${installDir} + LOG_DOWNLOAD YES +) + + +# Setup user config to force same c++ compiler in boost +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) + set(BOOST_UCONFIG "${CMAKE_BINARY_DIR}/user-config.jam") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + file(WRITE "${BOOST_UCONFIG}" "using gcc : : \"${CMAKE_CXX_COMPILER}\" ;\n") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + file(WRITE "${BOOST_UCONFIG}" "using clang : : \"${CMAKE_CXX_COMPILER}\" ;\n") + set(B2_OPTIONS ${B2_OPTIONS} "toolset=clang") + endif() + set(BOOST_PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${BOOST_UCONFIG} tools/build/src/user-config.jam) endif() -# Add googletest directly to our build. This defines -# the gtest and gtest_main targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build) -################################################################## +ExternalProject_Add(Boost + BUILD_IN_SOURCE YES + URL https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.bz2 + UPDATE_COMMAND "" + PATCH_COMMAND ${BOOST_PATCH_COMMAND} + CONFIGURE_COMMAND ./bootstrap.sh --prefix=${installDir}/lib + BUILD_COMMAND ./b2 install -j8 --prefix=${installDir} --with-filesystem --with-system --with-program_options link=static ${B2_OPTIONS} + INSTALL_COMMAND "" +) -ExternalProject_Add(zlib - PREFIX ${CMAKE_BINARY_DIR}/thirdparty/zlib - GIT_REPOSITORY "https://github.com/madler/zlib.git" - GIT_TAG "v1.2.8" +ExternalProject_Add(spdlog + GIT_REPOSITORY "https://github.com/gabime/spdlog.git" + GIT_TAG "v1.6.1" UPDATE_COMMAND "" - BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND ${CMAKE_BINARY_DIR}/thirdparty/zlib/src/zlib/configure --prefix=${CMAKE_BINARY_DIR}/thirdparty/zlib --static - INSTALL_DIR ${CMAKE_BINARY_DIR}/thirdparty/zlib - LOG_DOWNLOAD 1 - LOG_INSTALL 1 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${installDir} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} ) -ExternalProject_Add(htslib - PREFIX ${CMAKE_BINARY_DIR}/thirdparty/htslib - GIT_REPOSITORY "https://github.com/samtools/htslib.git" - GIT_TAG "1.3.1" - UPDATE_COMMAND "" - BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND "" - BUILD_COMMAND make - INSTALL_COMMAND make install prefix=${CMAKE_BINARY_DIR}/thirdparty/htslib - LOG_DOWNLOAD 1 + +ExternalProject_Add(googletest + GIT_REPOSITORY "https://github.com/google/googletest.git" + GIT_TAG "release-1.10.0" + UPDATE_COMMAND "" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${installDir} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} ) -include_directories(${CMAKE_BINARY_DIR}/thirdparty/zlib/include) -include_directories(${CMAKE_SOURCE_DIR}/thirdparty/spdlog/include) -set(zlib_static ${CMAKE_BINARY_DIR}/thirdparty/zlib/lib/libz.a) -set(htslib_static ${CMAKE_BINARY_DIR}/thirdparty/htslib/lib/libhts.a) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +# CMAKE_CXX_STANDARD is required for OS X 10.15 +ExternalProject_Add(abseil + GIT_REPOSITORY "https://github.com/abseil/abseil-cpp" + GIT_TAG "20210324.2" + UPDATE_COMMAND "" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${installDir} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_STANDARD=11 + -DBUILD_TESTING=OFF +) + + +ExternalProject_Add(ehunter + SOURCE_DIR ${CMAKE_SOURCE_DIR}/ehunter + BUILD_ALWAYS YES + TEST_COMMAND "ctest" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${installDir} + -DCMAKE_PREFIX_PATH:PATH=${installDir} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ${CMAKE_SOURCE_DIR}/ehunter +) + + +ExternalProject_Add_StepDependencies(ehunter configure + Boost spdlog htslib googletest abseil) -set(Boost_USE_STATIC_LIBS ON) -find_package(Boost 1.4 REQUIRED COMPONENTS program_options filesystem regex date_time system) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(SYSTEM ${Boost_INCLUDE_DIR}) -include_directories(${CMAKE_BINARY_DIR}/thirdparty/htslib/include) - -add_subdirectory(thirdparty/graph-tools-master) - -add_compile_options(-Werror -pedantic -Wall -Wextra) - -add_subdirectory(common) -add_subdirectory(genotyping) -add_subdirectory(reads) -add_subdirectory(classification) -add_subdirectory(region_spec) -add_subdirectory(region_analysis) -add_subdirectory(sample_analysis) -add_subdirectory(input) -add_subdirectory(output) -add_subdirectory(alignment) -add_subdirectory(stats) -add_subdirectory(filtering) - -file(GLOB SOURCES "src/*.cpp") -add_executable(ExpansionHunter ${SOURCES}) -target_compile_features(ExpansionHunter PRIVATE cxx_range_for) -target_link_libraries(ExpansionHunter graphtools common genotyping region_analysis region_spec sample_analysis input output alignment filtering stats pthread ${Boost_LIBRARIES}) -install (TARGETS ExpansionHunter DESTINATION bin) - -add_dependencies(htslib zlib) -add_dependencies(common htslib) diff --git a/alignment/CMakeLists.txt b/alignment/CMakeLists.txt deleted file mode 100644 index 02ef287..0000000 --- a/alignment/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(alignment ${SOURCES}) -target_link_libraries(alignment common graphtools input region_spec) - -add_subdirectory(tests) diff --git a/alignment/tests/CMakeLists.txt b/alignment/tests/CMakeLists.txt deleted file mode 100644 index f86a48c..0000000 --- a/alignment/tests/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -add_executable(HighQualityBaseRunFinderTest HighQualityBaseRunFinderTest.cpp) -target_link_libraries(HighQualityBaseRunFinderTest alignment gtest gmock_main) -add_test(NAME HighQualityBaseRunFinderTest COMMAND HighQualityBaseRunFinderTest) - -add_executable(GraphAlignmentOperationsTest GraphAlignmentOperationsTest.cpp) -target_link_libraries(GraphAlignmentOperationsTest alignment gtest gmock_main) -add_test(NAME GraphAlignmentOperationsTest COMMAND GraphAlignmentOperationsTest) - -add_executable(SoftclippingAlignerTest SoftclippingAlignerTest.cpp) -target_link_libraries(SoftclippingAlignerTest alignment gtest gmock_main) -add_test(NAME SoftclippingAlignerTest COMMAND SoftclippingAlignerTest) - -add_executable(GreedyAlignmentIntersectorTest GreedyAlignmentIntersectorTest.cpp) -target_link_libraries(GreedyAlignmentIntersectorTest alignment gtest gmock_main) -add_test(NAME GreedyAlignmentIntersectorTest COMMAND GreedyAlignmentIntersectorTest) diff --git a/classification/CMakeLists.txt b/classification/CMakeLists.txt deleted file mode 100644 index 406c9e7..0000000 --- a/classification/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(classification ${SOURCES}) -target_link_libraries(classification region_spec input graphtools reads) -add_subdirectory(tests) \ No newline at end of file diff --git a/classification/tests/CMakeLists.txt b/classification/tests/CMakeLists.txt deleted file mode 100644 index f6385b2..0000000 --- a/classification/tests/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_executable(AlignmentClassifierTest AlignmentClassifierTest.cpp) -target_link_libraries(AlignmentClassifierTest classification gtest_main) -add_test(NAME AlignmentClassifierTest COMMAND AlignmentClassifierTest) - -add_executable(AlignmentSummaryTest AlignmentSummaryTest.cpp) -target_link_libraries(AlignmentSummaryTest classification gtest_main) -add_test(NAME AlignmentSummaryTest COMMAND AlignmentSummaryTest) - -add_executable(ClassifierOfAlignmentsToVariantTest ClassifierOfAlignmentsToVariantTest.cpp) -target_link_libraries(ClassifierOfAlignmentsToVariantTest classification gtest_main) -add_test(NAME ClassifierOfAlignmentsToVariantTest COMMAND ClassifierOfAlignmentsToVariantTest) diff --git a/cmake/google_test.cmake b/cmake/google_test.cmake deleted file mode 100644 index 49ee88a..0000000 --- a/cmake/google_test.cmake +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 2.8.2) - -project(googletest-download NONE) - -include(ExternalProject) -ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.10.0 - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - ) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt deleted file mode 100644 index 3351a79..0000000 --- a/common/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(common ${SOURCES}) -target_link_libraries(common region_spec ${htslib_static} ${zlib_static} ${Boost_LIBRARIES}) -add_subdirectory(tests) diff --git a/common/HtsHelpers.cpp b/common/HtsHelpers.cpp deleted file mode 100644 index d12d20d..0000000 --- a/common/HtsHelpers.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "common/HtsHelpers.hh" - -#include -#include -#include -#include -#include -#include - -#include "thirdparty/spdlog/include/spdlog/spdlog.h" - -using std::pair; -using std::string; -using std::vector; - -namespace spd = spdlog; - -namespace ehunter -{ -namespace htshelpers -{ - - string decodeQuals(bam1_t* htsAlignPtr) - { - string quals; - uint8_t* htsQualsPtr = bam_get_qual(htsAlignPtr); - const int readLength = htsAlignPtr->core.l_qseq; - quals.resize(readLength); - - for (int index = 0; index < readLength; ++index) - { - quals[index] = static_cast(33 + htsQualsPtr[index]); - } - - return quals; - } - - string decodeBases(bam1_t* htsAlignPtr) - { - string bases; - uint8_t* htsSeqPtr = bam_get_seq(htsAlignPtr); - const int32_t readLength = htsAlignPtr->core.l_qseq; - bases.resize(readLength); - - for (int32_t index = 0; index < readLength; ++index) - { - bases[index] = seq_nt16_str[bam_seqi(htsSeqPtr, index)]; - } - - return bases; - } - - LinearAlignmentStats decodeAlignmentStats(bam1_t* htsAlignPtr) - { - LinearAlignmentStats alignmentStats; - alignmentStats.chromId = htsAlignPtr->core.tid; - alignmentStats.pos = htsAlignPtr->core.pos; - alignmentStats.mapq = htsAlignPtr->core.qual; - alignmentStats.mateChromId = htsAlignPtr->core.mtid; - alignmentStats.matePos = htsAlignPtr->core.mpos; - - uint32_t samFlag = htsAlignPtr->core.flag; - alignmentStats.isPaired = samFlag & BAM_FPAIRED; - alignmentStats.isMapped = !(samFlag & BAM_FUNMAP); - alignmentStats.isMateMapped = !(samFlag & BAM_FMUNMAP); - - return alignmentStats; - } - - bool isPrimaryAlignment(bam1_t* htsAlignPtr) - { - return !((htsAlignPtr->core.flag & BAM_FSECONDARY) || - (htsAlignPtr->core.flag & BAM_FSUPPLEMENTARY)); - } - - static string lowercaseLowQualityBases(const string& bases, const string& quals, int lowBaseQualityCutoff = 20) - { - const int qualityScoreOffset = 33; - string query = bases; - for (int index = 0; index != static_cast(bases.size()); ++index) - { - if (quals[index] - qualityScoreOffset <= lowBaseQualityCutoff) - { - query[index] = std::tolower(bases[index]); - } - } - return query; - } - - Read decodeRead(bam1_t* htsAlignPtr) - { - const uint32_t samFlag = htsAlignPtr->core.flag; - const bool isFirstMate = samFlag & BAM_FREAD1; - const bool isReversed = samFlag & BAM_FREVERSE; - - const string fragmentId = bam_get_qname(htsAlignPtr); - MateNumber mateNumber = isFirstMate ? MateNumber::kFirstMate : MateNumber::kSecondMate; - ReadId readId(fragmentId, mateNumber); - - string bases = decodeBases(htsAlignPtr); - string quals = decodeQuals(htsAlignPtr); - string sequence = lowercaseLowQualityBases(bases, quals); - - return Read(readId, sequence, isReversed); - } - - ReferenceContigInfo decodeContigInfo(bam_hdr_t* htsHeaderPtr) - { - vector> contigNamesAndSizes; - const int32_t numContigs = htsHeaderPtr->n_targets; - contigNamesAndSizes.reserve(numContigs); - - for (int32_t contigIndex = 0; contigIndex != numContigs; ++contigIndex) - { - const string contig = htsHeaderPtr->target_name[contigIndex]; - int64_t size = htsHeaderPtr->target_len[contigIndex]; - contigNamesAndSizes.push_back(std::make_pair(contig, size)); - } - - return ReferenceContigInfo(contigNamesAndSizes); - } - -} // namespace htshelpers -} diff --git a/common/tests/CMakeLists.txt b/common/tests/CMakeLists.txt deleted file mode 100644 index 6fb5db6..0000000 --- a/common/tests/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(CountTableTest CountTableTest.cpp) -target_link_libraries(CountTableTest common gtest_main) -add_test(NAME CountTableTest COMMAND CountTableTest) - -add_executable(GenomicRegionTest GenomicRegionTest.cpp) -target_link_libraries(GenomicRegionTest common gtest_main) -add_test(NAME GenomicRegionTest COMMAND GenomicRegionTest) diff --git a/docs/02_Installation.md b/docs/02_Installation.md index 69621cd..6d9ec49 100644 --- a/docs/02_Installation.md +++ b/docs/02_Installation.md @@ -9,19 +9,23 @@ build the program from source follow the instructions below. Prerequisites: - - A recent version of [GCC](https://gcc.gnu.org/) or - [clang](http://clang.llvm.org/) compiler supporting C++11 standard - - [CMake](https://cmake.org/) version 3.5.0 or above - - [Boost C++ Libraries](http://www.boost.org/) version 1.57.0 or - above - -If you the above prerequisites are satisfied, you are ready to -build the program. Note that during the build procedure, cmake will -attempt to download and install [HTSlib](http://www.htslib.org) and -[zlib](https://github.com/madler/zlib) so an active internet -connection is required. Assuming that the source code is contained -in a directory `ExpansionHunter/`, the build procedure can be -initiated as follows: + - A recent version of [GCC](https://gcc.gnu.org/) or + [clang](http://clang.llvm.org/) compiler supporting the C++11 standard + - [CMake](https://cmake.org/) version 3.13.0 or above + - Additional development libraries, which depend on the operating system: + - Centos7 + - `yum install bzip2-devel libcurl-devel libstdc++-static xz-devel zlib-devel` + - Ubuntu 20.04 + - `apt install zlib1g libbz2-dev liblzma-dev libcurl4-openssl-dev` + - macOS 10.15 + - `brew install xz` + +If the above prerequisites are satisfied, you are ready to +build the program. Note that during the build procedure, cmake will +attempt to download and install `abseil`, `boost`, `googletest`, `htslib`, +and `spdlog` so an active internet connection is required. Assuming +that the source code is contained in a directory `ExpansionHunter/`, +the build procedure can be initiated as follows: ```bash $ cd ExpansionHunter @@ -31,12 +35,6 @@ $ cmake .. $ make ``` -Note that if Boost is installed in a non-default location then its path should -be specified with `BOOST_ROOT` in the cmake command above: +If all the above steps were successful, the ExpansionHunter executable can be found in: -```bash -$ cmake -DBOOST_ROOT=/path/to/boost/ .. -``` - -If all of the above steps were successful, the `build` directory now contains -ExpansionHunter executable. \ No newline at end of file + build/install/bin/ExpansionHunter diff --git a/docs/03_Usage.md b/docs/03_Usage.md index fcc21d8..c872cbf 100644 --- a/docs/03_Usage.md +++ b/docs/03_Usage.md @@ -29,5 +29,29 @@ optional arguments. * `--region-extension-length ` Specifies how far from on/off-target regions to search for informative reads. Set to 1000 by default. +* `--analysis-mode ` Specify analysis mode, which can be either `seeking` or + `streaming`. The default mode is `seeking`. See further description of analysis + modes below. + +* `--threads ` Specifies how many threads to can be used accelerate analysis + of large variant catalogs. Set to 1 by default. Typically seeking mode can + benefit from relatively high thread counts, while for streaming mode + there is limited benefit beyond about 16 threads. + Note that the full list of program options with brief explanations can be -obtained by running `ExpansionHunter --help`. \ No newline at end of file +obtained by running `ExpansionHunter --help`. + +### Analysis modes + +#### Seeking mode + +In seeking mode, alignment file indexing is used to seek specific read sets for the +analysis of each variant. Seeking mode is recommended for analysis of small catalogs. +This mode requires that the input BAM or CRAM file is already sorted and indexed. + +#### Streaming mode + +In streaming mode, the alignment file is read in a single pass and all variants are +analyzed during this reading operation. Streaming mode is recommended for the analysis +of large catalogs, but does require more memory as a funciton of catalog size. This mode +does not require that the BAM or CRAM file is sorted or indexed. diff --git a/.clang-format b/ehunter/.clang-format similarity index 86% rename from .clang-format rename to ehunter/.clang-format index d1695da..48154cc 100644 --- a/.clang-format +++ b/ehunter/.clang-format @@ -5,3 +5,4 @@ BreakBeforeBraces: Allman BreakStringLiterals: true ReflowComments: true CompactNamespaces: true +NamespaceIndentation: None diff --git a/ehunter/CMakeLists.txt b/ehunter/CMakeLists.txt new file mode 100644 index 0000000..de31781 --- /dev/null +++ b/ehunter/CMakeLists.txt @@ -0,0 +1,164 @@ +cmake_minimum_required(VERSION 3.13) +project(ExpansionHunter LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (NOT CMAKE_BUILD_TYPE) + set(DEFAULT_CMAKE_BUILD_TYPE Release) + set(CMAKE_BUILD_TYPE ${DEFAULT_CMAKE_BUILD_TYPE} CACHE STRING + "Choose the type of build (default: ${DEFAULT_CMAKE_BUILD_TYPE})" FORCE) +endif () + +message(STATUS "BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +set(Boost_USE_STATIC_LIBS ON) +find_package(Boost 1.73 REQUIRED COMPONENTS program_options filesystem system) + + +add_compile_options(-Wall -Werror -Wextra) + +enable_testing() + +find_package(ZLIB REQUIRED) +find_package(BZip2 REQUIRED) +find_package(LibLZMA REQUIRED) +find_package(CURL REQUIRED) +find_package(GTest REQUIRED) +find_package(Threads REQUIRED) +find_package(spdlog REQUIRED) +find_package(absl REQUIRED) + +find_library(htslib libhts.a) +find_library(htslib hts) + +add_subdirectory(thirdparty/graph-tools-master-f421f4c) + +add_library(ExpansionHunterLib + alignment/AlignmentClassifier.hh alignment/AlignmentClassifier.cpp + alignment/AlignmentFilters.hh alignment/AlignmentFilters.cpp + alignment/ClassifierOfAlignmentsToVariant.hh alignment/ClassifierOfAlignmentsToVariant.cpp + alignment/GraphVariantAlignmentStats.hh alignment/GraphVariantAlignmentStats.cpp + alignment/GreedyAlignmentIntersector.hh alignment/GreedyAlignmentIntersector.cpp + alignment/HighQualityBaseRunFinder.hh alignment/HighQualityBaseRunFinder.cpp + alignment/OperationsOnAlignments.hh alignment/OperationsOnAlignments.cpp + alignment/OrientationPredictor.hh alignment/OrientationPredictor.cpp + alignment/SoftclippingAligner.hh alignment/SoftclippingAligner.cpp + core/Common.hh core/Common.cpp + core/ConcurrentQueue.hh + core/CountTable.hh core/CountTable.cpp + core/GenomicRegion.hh core/GenomicRegion.cpp + core/HtsHelpers.hh core/HtsHelpers.cpp + core/Parameters.hh core/Parameters.cpp + core/Reference.hh core/Reference.cpp + core/ReferenceContigInfo.hh core/ReferenceContigInfo.cpp + core/LocusStats.hh core/LocusStats.cpp + core/LogSum.hh + core/Read.hh core/Read.cpp + core/ReadPairs.hh core/ReadPairs.cpp + core/ReadSupportCalculator.hh core/ReadSupportCalculator.cpp + core/ThreadPool.hh + core/WeightedPurityCalculator.hh core/WeightedPurityCalculator.cpp + genotyping/AlignMatrix.hh genotyping/AlignMatrix.cpp + genotyping/AlignMatrixFiltering.hh genotyping/AlignMatrixFiltering.cpp + genotyping/AlleleChecker.hh genotyping/AlleleChecker.cpp + genotyping/FragLogliks.hh genotyping/FragLogliks.cpp + genotyping/OneAlleleStrGenotyper.hh genotyping/OneAlleleStrGenotyper.cpp + genotyping/RepeatGenotype.hh genotyping/RepeatGenotype.cpp + genotyping/SmallVariantGenotype.hh genotyping/SmallVariantGenotype.cpp + genotyping/SmallVariantGenotyper.hh genotyping/SmallVariantGenotyper.cpp + genotyping/StrAlign.hh genotyping/StrAlign.cpp + genotyping/StrGenotyper.hh genotyping/StrGenotyper.cpp + genotyping/TwoAlleleStrGenotyper.hh genotyping/TwoAlleleStrGenotyper.cpp + io/BamletWriter.hh io/BamletWriter.cpp + io/CatalogLoading.hh io/CatalogLoading.cpp + io/GraphBlueprint.hh io/GraphBlueprint.cpp + io/JsonWriter.hh io/JsonWriter.cpp + io/LocusSpecDecoding.hh io/LocusSpecDecoding.cpp + io/ParameterLoading.hh io/ParameterLoading.cpp + io/RegionGraph.hh io/RegionGraph.cpp + io/SampleStats.hh io/SampleStats.cpp + io/VcfHeader.hh io/VcfHeader.cpp + io/VcfWriter.hh io/VcfWriter.cpp + io/VcfWriterHelpers.hh io/VcfWriterHelpers.cpp + sample/AnalyzerFinder.hh sample/AnalyzerFinder.cpp + sample/GenomeMask.hh sample/GenomeMask.cpp + sample/GenomeQueryCollection.hh sample/GenomeQueryCollection.cpp + sample/HtsFileSeeker.hh sample/HtsFileSeeker.cpp + sample/HtsFileStreamer.hh sample/HtsFileStreamer.cpp + sample/HtsSeekingSampleAnalysis.hh sample/HtsSeekingSampleAnalysis.cpp + sample/HtsStreamingReadPairQueue.hh sample/HtsStreamingReadPairQueue.cpp + sample/HtsStreamingSampleAnalysis.hh sample/HtsStreamingSampleAnalysis.cpp + sample/IndexBasedDepthEstimate.hh sample/IndexBasedDepthEstimate.cpp + sample/MateExtractor.hh sample/MateExtractor.cpp + ) + + +set(CTPL_INCLUDE_DIRS thirdparty/ctpl/ctpl-0.0.2) +target_include_directories(ExpansionHunterLib PUBLIC + ${CMAKE_SOURCE_DIR} + ${Boost_INCLUDE_DIRS} + ${LIBLZMA_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${CTPL_INCLUDE_DIRS} + ) + +# Set static linking of gcc standard libraries to simplify binary distribution +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(STATIC_FLAGS -static-libgcc -static-libstdc++) +endif() + + +target_link_libraries(ExpansionHunterLib PUBLIC + ${STATIC_FLAGS} + graphtools + ${htslib} + ${Boost_LIBRARIES} + ${LIBLZMA_LIBRARIES} + ${CURL_LIBRARIES} + ZLIB::ZLIB + BZip2::BZip2 + Threads::Threads + spdlog::spdlog + absl::flat_hash_set) + +add_executable(ExpansionHunter + app/ExpansionHunter.cpp + ) +target_link_libraries(ExpansionHunter ExpansionHunterLib) + +add_executable(UnitTests + tests/AlignMatrixTest.cpp + tests/AlignmentClassifierTest.cpp + tests/AlignmentSummaryTest.cpp + tests/AlleleCheckerTest.cpp + tests/ClassifierOfAlignmentsToVariantTest.cpp + tests/ConcurrentQueueTest.cpp + tests/CountTableTest.cpp + tests/FragLogliksTest.cpp + tests/GenomeMaskTest.cpp + tests/GenomicRegionTest.cpp + tests/GraphAlignmentOperationsTest.cpp + tests/GraphBlueprintTest.cpp + tests/GreedyAlignmentIntersectorTest.cpp + tests/HighQualityBaseRunFinderTest.cpp + tests/LocusStatsTest.cpp + tests/ReadSupportCalculatorTest.cpp + tests/ReadTest.cpp + tests/RegionGraphTest.cpp + tests/RepeatAnalyzerTest.cpp + tests/RepeatGenotypeTest.cpp + tests/SmallVariantGenotyperTest.cpp + tests/SoftclippingAlignerTest.cpp + tests/StrAlignTest.cpp + tests/StrGenotyperTest.cpp + tests/UnitTests.cpp + tests/WeightedPurityCalculatorTest.cpp + ) +add_subdirectory(locus) + +target_link_libraries(UnitTests ExpansionHunterLib GTest::GTest GTest::Main) + +add_test(NAME UnitTests COMMAND UnitTests) + +install(TARGETS ExpansionHunter UnitTests RUNTIME DESTINATION bin) diff --git a/classification/AlignmentClassifier.cpp b/ehunter/alignment/AlignmentClassifier.cpp similarity index 98% rename from classification/AlignmentClassifier.cpp rename to ehunter/alignment/AlignmentClassifier.cpp index 70a91dd..4c39b27 100644 --- a/classification/AlignmentClassifier.cpp +++ b/ehunter/alignment/AlignmentClassifier.cpp @@ -23,7 +23,7 @@ #include #include -#include "classification/AlignmentClassifier.hh" +#include "alignment/AlignmentClassifier.hh" using graphtools::NodeId; using graphtools::Path; diff --git a/classification/AlignmentClassifier.hh b/ehunter/alignment/AlignmentClassifier.hh similarity index 100% rename from classification/AlignmentClassifier.hh rename to ehunter/alignment/AlignmentClassifier.hh diff --git a/alignment/AlignmentFilters.cpp b/ehunter/alignment/AlignmentFilters.cpp similarity index 100% rename from alignment/AlignmentFilters.cpp rename to ehunter/alignment/AlignmentFilters.cpp diff --git a/alignment/AlignmentFilters.hh b/ehunter/alignment/AlignmentFilters.hh similarity index 100% rename from alignment/AlignmentFilters.hh rename to ehunter/alignment/AlignmentFilters.hh diff --git a/classification/ClassifierOfAlignmentsToVariant.cpp b/ehunter/alignment/ClassifierOfAlignmentsToVariant.cpp similarity index 98% rename from classification/ClassifierOfAlignmentsToVariant.cpp rename to ehunter/alignment/ClassifierOfAlignmentsToVariant.cpp index fafc1c3..28c3332 100644 --- a/classification/ClassifierOfAlignmentsToVariant.cpp +++ b/ehunter/alignment/ClassifierOfAlignmentsToVariant.cpp @@ -19,7 +19,7 @@ // // -#include "classification/ClassifierOfAlignmentsToVariant.hh" +#include "alignment/ClassifierOfAlignmentsToVariant.hh" #include #include diff --git a/classification/ClassifierOfAlignmentsToVariant.hh b/ehunter/alignment/ClassifierOfAlignmentsToVariant.hh similarity index 97% rename from classification/ClassifierOfAlignmentsToVariant.hh rename to ehunter/alignment/ClassifierOfAlignmentsToVariant.hh index 8f6b571..6eb9576 100644 --- a/classification/ClassifierOfAlignmentsToVariant.hh +++ b/ehunter/alignment/ClassifierOfAlignmentsToVariant.hh @@ -27,10 +27,10 @@ #include "graphalign/GraphAlignment.hh" #include "graphcore/Graph.hh" -#include "common/CountTable.hh" - -namespace ehunter { +#include "core/CountTable.hh" +namespace ehunter +{ class ClassifierOfAlignmentsToVariant { diff --git a/filtering/GraphVariantAlignmentStats.cpp b/ehunter/alignment/GraphVariantAlignmentStats.cpp similarity index 98% rename from filtering/GraphVariantAlignmentStats.cpp rename to ehunter/alignment/GraphVariantAlignmentStats.cpp index 60ec3f5..1a2fe21 100644 --- a/filtering/GraphVariantAlignmentStats.cpp +++ b/ehunter/alignment/GraphVariantAlignmentStats.cpp @@ -19,7 +19,7 @@ // // -#include "filtering/GraphVariantAlignmentStats.hh" +#include "alignment/GraphVariantAlignmentStats.hh" #include @@ -146,4 +146,4 @@ std::ostream& operator<<(std::ostream& out, const GraphVariantAlignmentStats& st return out; } -} \ No newline at end of file +} diff --git a/filtering/GraphVariantAlignmentStats.hh b/ehunter/alignment/GraphVariantAlignmentStats.hh similarity index 100% rename from filtering/GraphVariantAlignmentStats.hh rename to ehunter/alignment/GraphVariantAlignmentStats.hh diff --git a/alignment/GreedyAlignmentIntersector.cpp b/ehunter/alignment/GreedyAlignmentIntersector.cpp similarity index 100% rename from alignment/GreedyAlignmentIntersector.cpp rename to ehunter/alignment/GreedyAlignmentIntersector.cpp diff --git a/alignment/GreedyAlignmentIntersector.hh b/ehunter/alignment/GreedyAlignmentIntersector.hh similarity index 100% rename from alignment/GreedyAlignmentIntersector.hh rename to ehunter/alignment/GreedyAlignmentIntersector.hh diff --git a/alignment/HighQualityBaseRunFinder.cpp b/ehunter/alignment/HighQualityBaseRunFinder.cpp similarity index 100% rename from alignment/HighQualityBaseRunFinder.cpp rename to ehunter/alignment/HighQualityBaseRunFinder.cpp diff --git a/alignment/HighQualityBaseRunFinder.hh b/ehunter/alignment/HighQualityBaseRunFinder.hh similarity index 100% rename from alignment/HighQualityBaseRunFinder.hh rename to ehunter/alignment/HighQualityBaseRunFinder.hh diff --git a/alignment/OperationsOnAlignments.cpp b/ehunter/alignment/OperationsOnAlignments.cpp similarity index 100% rename from alignment/OperationsOnAlignments.cpp rename to ehunter/alignment/OperationsOnAlignments.cpp diff --git a/alignment/OperationsOnAlignments.hh b/ehunter/alignment/OperationsOnAlignments.hh similarity index 100% rename from alignment/OperationsOnAlignments.hh rename to ehunter/alignment/OperationsOnAlignments.hh diff --git a/filtering/OrientationPredictor.cpp b/ehunter/alignment/OrientationPredictor.cpp similarity index 88% rename from filtering/OrientationPredictor.cpp rename to ehunter/alignment/OrientationPredictor.cpp index 5d9fa7f..8365b3a 100644 --- a/filtering/OrientationPredictor.cpp +++ b/ehunter/alignment/OrientationPredictor.cpp @@ -19,18 +19,17 @@ // // -#include "filtering/OrientationPredictor.hh" +#include "alignment/OrientationPredictor.hh" #include #include #include -#include #include #include "graphcore/Path.hh" #include "graphcore/PathOperations.hh" +#include "graphutils/SequenceOperations.hh" -using graphtools::GraphAlignment; using graphtools::KmerIndex; using graphtools::Path; using std::list; @@ -40,7 +39,7 @@ using std::vector; namespace ehunter { -static int countNonoverlappingKmerMatches(const string query, const KmerIndex& kmerIndex) +static int countNonoverlappingKmerMatches(const string& query, const KmerIndex& kmerIndex) { const std::size_t kmerLength = kmerIndex.kmerLength(); int matchCount = 0; @@ -67,7 +66,7 @@ OrientationPrediction OrientationPredictor::predict(const std::string& query) co { const int numForwardMatches = countNonoverlappingKmerMatches(query, kmerIndex_); const int numReverseComplementMatches - = countNonoverlappingKmerMatches(query, kmerIndexForReverseComplementedGraph_); + = countNonoverlappingKmerMatches(graphtools::reverseComplement(query), kmerIndex_); const int maxMatches = std::max(numForwardMatches, numReverseComplementMatches); @@ -91,7 +90,7 @@ std::ostream& operator<<(std::ostream& out, OrientationPrediction orientationPre switch (orientationPrediction) { case OrientationPrediction::kAlignsInOriginalOrientation: - out << "kAlignsInReverseComplementOrientation"; + out << "kAlignsInOriginalOrientation"; break; case OrientationPrediction::kAlignsInReverseComplementOrientation: out << "kAlignsInReverseComplementOrientation"; diff --git a/filtering/OrientationPredictor.hh b/ehunter/alignment/OrientationPredictor.hh similarity index 71% rename from filtering/OrientationPredictor.hh rename to ehunter/alignment/OrientationPredictor.hh index e8ba203..37e3f75 100644 --- a/filtering/OrientationPredictor.hh +++ b/ehunter/alignment/OrientationPredictor.hh @@ -22,13 +22,11 @@ #pragma once #include -#include -#include +#include +#include -#include "graphalign/GraphAlignment.hh" #include "graphalign/KmerIndex.hh" #include "graphcore/Graph.hh" -#include "graphcore/GraphOperations.hh" namespace ehunter { @@ -45,12 +43,11 @@ std::ostream& operator<<(std::ostream& out, OrientationPrediction orientationPre class OrientationPredictor { public: - OrientationPredictor(const graphtools::Graph* graphRawPtr) - : kmerLength_(10) - , minKmerMatchesToPass_(3) + explicit OrientationPredictor( + const graphtools::Graph* graphRawPtr, int kmerLength = 10, int minKmerMatchesToPass = 3) + : kmerLength_(kmerLength) + , minKmerMatchesToPass_(minKmerMatchesToPass) , kmerIndex_(*graphRawPtr, kmerLength_) - , reverseComplementedGraph_(graphtools::reverseGraph(*graphRawPtr, true)) - , kmerIndexForReverseComplementedGraph_(reverseComplementedGraph_, kmerLength_) { } @@ -60,8 +57,6 @@ private: int32_t kmerLength_; int32_t minKmerMatchesToPass_; graphtools::KmerIndex kmerIndex_; - const graphtools::Graph reverseComplementedGraph_; - graphtools::KmerIndex kmerIndexForReverseComplementedGraph_; }; } diff --git a/alignment/SoftclippingAligner.cpp b/ehunter/alignment/SoftclippingAligner.cpp similarity index 89% rename from alignment/SoftclippingAligner.cpp rename to ehunter/alignment/SoftclippingAligner.cpp index d25ab9a..29c3443 100644 --- a/alignment/SoftclippingAligner.cpp +++ b/ehunter/alignment/SoftclippingAligner.cpp @@ -21,8 +21,8 @@ #include "alignment/SoftclippingAligner.hh" -#include "alignment/OperationsOnAlignments.hh" #include "alignment/HighQualityBaseRunFinder.hh" +#include "alignment/OperationsOnAlignments.hh" using graphtools::Graph; using graphtools::GraphAlignment; @@ -33,13 +33,12 @@ namespace ehunter { SoftclippingAligner::SoftclippingAligner( - const Graph* graphPtr, const std::string& alignerName, int kmerLenForAlignment, int paddingLength, - int seedAffixTrimLength) - : aligner_(graphPtr, kmerLenForAlignment, paddingLength, seedAffixTrimLength, alignerName) + const Graph* graphPtr, int kmerLenForAlignment, int paddingLength, int seedAffixTrimLength) + : aligner_(graphPtr, kmerLenForAlignment, paddingLength, seedAffixTrimLength) { } -list SoftclippingAligner::align(const string& query) const +list SoftclippingAligner::align(const string& query, graphtools::AlignerSelector& alignerSelector) const { /* HighQualityBaseRunFinder goodBaseFinder(6, 2, query.size() / 2); @@ -57,7 +56,7 @@ list SoftclippingAligner::align(const string& query) const } */ - return aligner_.align(query); + return aligner_.align(query, alignerSelector); } } diff --git a/alignment/SoftclippingAligner.hh b/ehunter/alignment/SoftclippingAligner.hh similarity index 80% rename from alignment/SoftclippingAligner.hh rename to ehunter/alignment/SoftclippingAligner.hh index af77ce2..e4d5a8d 100644 --- a/alignment/SoftclippingAligner.hh +++ b/ehunter/alignment/SoftclippingAligner.hh @@ -35,9 +35,9 @@ class SoftclippingAligner { public: SoftclippingAligner( - const graphtools::Graph* graphPtr, const std::string& alignerName, int kmerLenForAlignment, int paddingLength, - int seedAffixTrimLength); - std::list align(const std::string& query) const; + const graphtools::Graph* graphPtr, int kmerLenForAlignment, int paddingLength, int seedAffixTrimLength); + std::list + align(const std::string& query, graphtools::AlignerSelector& alignerSelector) const; private: graphtools::GappedGraphAligner aligner_; diff --git a/src/ExpansionHunter.cpp b/ehunter/app/ExpansionHunter.cpp similarity index 80% rename from src/ExpansionHunter.cpp rename to ehunter/app/ExpansionHunter.cpp index 1d40fca..38bba38 100644 --- a/src/ExpansionHunter.cpp +++ b/ehunter/app/ExpansionHunter.cpp @@ -32,21 +32,21 @@ // clang-format off // Note that spdlog.h must be included before ostr.h -#include "thirdparty/spdlog/include/spdlog/spdlog.h" +#include "spdlog/spdlog.h" //#include "thirdparty/spdlog/include/spdlog/fmt/ostr.h" // clang-format on -#include "common/Parameters.hh" -#include "input/CatalogLoading.hh" -#include "input/ParameterLoading.hh" -#include "input/SampleStats.hh" -#include "output/BamletWriter.hh" -#include "output/JsonWriter.hh" -#include "output/VcfWriter.hh" -#include "region_analysis/VariantFindings.hh" -#include "sample_analysis/HtsSeekingSampleAnalysis.hh" -#include "sample_analysis/HtsStreamingSampleAnalysis.hh" -#include "src/Version.hh" +#include "app/Version.hh" +#include "core/Parameters.hh" +#include "io/BamletWriter.hh" +#include "io/CatalogLoading.hh" +#include "io/JsonWriter.hh" +#include "io/ParameterLoading.hh" +#include "io/SampleStats.hh" +#include "io/VcfWriter.hh" +#include "locus/VariantFindings.hh" +#include "sample/HtsSeekingSampleAnalysis.hh" +#include "sample/HtsStreamingSampleAnalysis.hh" namespace spd = spdlog; @@ -100,11 +100,11 @@ int main(int argc, char** argv) { return 0; } - ProgramParameters& params = *optionalProgramParameters; + const ProgramParameters& params = *optionalProgramParameters; setLogLevel(params.logLevel()); - SampleParameters& sampleParams = params.sample(); + const SampleParameters& sampleParams = params.sample(); spdlog::info("Analyzing sample {}", sampleParams.id()); @@ -119,20 +119,28 @@ int main(int argc, char** argv) const OutputPaths& outputPaths = params.outputPaths(); - BamletWriter bamletWriter(outputPaths.bamlet(), reference.contigInfo(), regionCatalog); + locus::AlignWriterPtr bamletWriter; + if (params.disableBamletOutput) + { + bamletWriter.reset(new graphtools::BlankAlignmentWriter()); + } + else + { + bamletWriter.reset(new BamletWriter(outputPaths.bamlet(), reference.contigInfo(), regionCatalog)); + } SampleFindings sampleFindings; if (params.analysisMode() == AnalysisMode::kSeeking) { spdlog::info("Running sample analysis in seeking mode"); sampleFindings = htsSeekingSampleAnalysis( - inputPaths, sampleParams.sex(), heuristicParams, regionCatalog, bamletWriter); + inputPaths, sampleParams.sex(), heuristicParams, params.threadCount, regionCatalog, bamletWriter); } else { spdlog::info("Running sample analysis in streaming mode"); sampleFindings = htsStreamingSampleAnalysis( - inputPaths, sampleParams.sex(), heuristicParams, regionCatalog, bamletWriter); + inputPaths, sampleParams.sex(), heuristicParams, params.threadCount, regionCatalog, bamletWriter); } spdlog::info("Writing output to disk"); diff --git a/src/Version.hh b/ehunter/app/Version.hh similarity index 93% rename from src/Version.hh rename to ehunter/app/Version.hh index 15cb5f5..b0e4a02 100644 --- a/src/Version.hh +++ b/ehunter/app/Version.hh @@ -28,6 +28,6 @@ namespace ehunter { -const std::string kProgramVersion = "ExpansionHunter v4.0.2"; +const std::string kProgramVersion = "ExpansionHunter v5.0.0"; } diff --git a/common/Common.cpp b/ehunter/core/Common.cpp similarity index 98% rename from common/Common.cpp rename to ehunter/core/Common.cpp index 04b8fa6..6c3b711 100644 --- a/common/Common.cpp +++ b/ehunter/core/Common.cpp @@ -21,7 +21,7 @@ // // -#include "common/Common.hh" +#include "core/Common.hh" using std::string; diff --git a/common/Common.hh b/ehunter/core/Common.hh similarity index 94% rename from common/Common.hh rename to ehunter/core/Common.hh index 5f374ef..649cac2 100644 --- a/common/Common.hh +++ b/ehunter/core/Common.hh @@ -79,13 +79,6 @@ public: int start() const { return start_; } int end() const { return end_; } - NumericInterval& operator=(const NumericInterval& other) - { - start_ = other.start_; - end_ = other.end_; - return *this; - } - bool operator==(const NumericInterval& other) const { return start_ == other.start_ && end_ == other.end_; } private: diff --git a/ehunter/core/ConcurrentQueue.hh b/ehunter/core/ConcurrentQueue.hh new file mode 100644 index 0000000..7940761 --- /dev/null +++ b/ehunter/core/ConcurrentQueue.hh @@ -0,0 +1,67 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include +#include +#include + +namespace ehunter +{ + +template class ConcurrentQueue +{ +public: + void push(T const& data) + { + { + std::lock_guard lock(mutex_); + queue_.push(data); + } + cv_.notify_one(); + } + + bool empty() const + { + std::lock_guard lock(mutex_); + return queue_.empty(); + } + + void pop(T& value) + { + std::unique_lock lock(mutex_); + while (queue_.empty()) + { + cv_.wait(lock); + } + + value = queue_.front(); + queue_.pop(); + } + +private: + std::queue queue_; + mutable std::mutex mutex_; + std::condition_variable cv_; +}; + +} diff --git a/common/CountTable.cpp b/ehunter/core/CountTable.cpp similarity index 98% rename from common/CountTable.cpp rename to ehunter/core/CountTable.cpp index 859f12b..c6941d5 100644 --- a/common/CountTable.cpp +++ b/ehunter/core/CountTable.cpp @@ -20,7 +20,7 @@ // // -#include "common/CountTable.hh" +#include "core/CountTable.hh" #include diff --git a/common/CountTable.hh b/ehunter/core/CountTable.hh similarity index 100% rename from common/CountTable.hh rename to ehunter/core/CountTable.hh diff --git a/common/GenomicRegion.cpp b/ehunter/core/GenomicRegion.cpp similarity index 99% rename from common/GenomicRegion.cpp rename to ehunter/core/GenomicRegion.cpp index 6c9732b..51e9e83 100644 --- a/common/GenomicRegion.cpp +++ b/ehunter/core/GenomicRegion.cpp @@ -21,7 +21,7 @@ // // -#include "common/GenomicRegion.hh" +#include "core/GenomicRegion.hh" #include #include diff --git a/common/GenomicRegion.hh b/ehunter/core/GenomicRegion.hh similarity index 98% rename from common/GenomicRegion.hh rename to ehunter/core/GenomicRegion.hh index 3ec829d..3c5821d 100644 --- a/common/GenomicRegion.hh +++ b/ehunter/core/GenomicRegion.hh @@ -28,7 +28,7 @@ #include #include -#include "common/ReferenceContigInfo.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { diff --git a/ehunter/core/HtsHelpers.cpp b/ehunter/core/HtsHelpers.cpp new file mode 100644 index 0000000..1dff013 --- /dev/null +++ b/ehunter/core/HtsHelpers.cpp @@ -0,0 +1,161 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "core/HtsHelpers.hh" + +#include +#include +#include +#include +#include +#include + +#include "spdlog/spdlog.h" + +using std::pair; +using std::string; +using std::vector; + +namespace spd = spdlog; + +namespace ehunter +{ +namespace htshelpers +{ + +string decodeQuals(bam1_t* htsAlignPtr) +{ + string quals; + uint8_t* htsQualsPtr = bam_get_qual(htsAlignPtr); + const int readLength = htsAlignPtr->core.l_qseq; + quals.resize(readLength); + + for (int index = 0; index < readLength; ++index) + { + quals[index] = static_cast(33 + htsQualsPtr[index]); + } + + return quals; +} + +string decodeBases(bam1_t* htsAlignPtr) +{ + string bases; + uint8_t* htsSeqPtr = bam_get_seq(htsAlignPtr); + const int32_t readLength = htsAlignPtr->core.l_qseq; + bases.resize(readLength); + + for (int32_t index = 0; index < readLength; ++index) + { + bases[index] = seq_nt16_str[bam_seqi(htsSeqPtr, index)]; + } + + return bases; +} + +LinearAlignmentStats decodeAlignmentStats(bam1_t* htsAlignPtr) +{ + LinearAlignmentStats alignmentStats; + alignmentStats.chromId = htsAlignPtr->core.tid; + alignmentStats.pos = htsAlignPtr->core.pos; + alignmentStats.mapq = htsAlignPtr->core.qual; + alignmentStats.mateChromId = htsAlignPtr->core.mtid; + alignmentStats.matePos = htsAlignPtr->core.mpos; + + uint32_t samFlag = htsAlignPtr->core.flag; + alignmentStats.isPaired = samFlag & BAM_FPAIRED; + alignmentStats.isMapped = !(samFlag & BAM_FUNMAP); + alignmentStats.isMateMapped = !(samFlag & BAM_FMUNMAP); + + return alignmentStats; +} + +bool isPrimaryAlignment(bam1_t* htsAlignPtr) +{ + return !((htsAlignPtr->core.flag & BAM_FSECONDARY) || (htsAlignPtr->core.flag & BAM_FSUPPLEMENTARY)); +} + +/// Lower-case version of htslib seq_nt16_str table; +class Cache_seq_nt16_str_lc +{ +public: + Cache_seq_nt16_str_lc() + { + for (unsigned i(0); i < 16; ++i) + { + data[i] = std::tolower(seq_nt16_str[i]); + } + } + + char data[16]; +}; + +static Cache_seq_nt16_str_lc seq_nt16_str_lc; + +Read decodeRead(bam1_t* htsAlignPtr) +{ + const uint32_t samFlag = htsAlignPtr->core.flag; + const bool isFirstMate = samFlag & BAM_FREAD1; + const bool isReversed = samFlag & BAM_FREVERSE; + + const char* qname(bam_get_qname(htsAlignPtr)); + MateNumber mateNumber = isFirstMate ? MateNumber::kFirstMate : MateNumber::kSecondMate; + ReadId readId(qname, mateNumber); + + string bases; + + { + // Decode bases and convert low-quality bases to lowercase: + static const uint8_t lowBaseQualityCutoff(20); + + const uint8_t* htsSeqPtr = bam_get_seq(htsAlignPtr); + const uint8_t* htsQualPtr = bam_get_qual(htsAlignPtr); + const int32_t readLength = htsAlignPtr->core.l_qseq; + bases.resize(readLength); + + for (int32_t index = 0; index < readLength; ++index) + { + const char* converter((htsQualPtr[index] <= lowBaseQualityCutoff) ? seq_nt16_str_lc.data : seq_nt16_str); + bases[index] = converter[bam_seqi(htsSeqPtr, index)]; + } + } + + return { std::move(readId), std::move(bases), isReversed }; +} + +ReferenceContigInfo decodeContigInfo(bam_hdr_t* htsHeaderPtr) +{ + vector> contigNamesAndSizes; + const int32_t numContigs = htsHeaderPtr->n_targets; + contigNamesAndSizes.reserve(numContigs); + + for (int32_t contigIndex = 0; contigIndex != numContigs; ++contigIndex) + { + const string contig = htsHeaderPtr->target_name[contigIndex]; + int64_t size = htsHeaderPtr->target_len[contigIndex]; + contigNamesAndSizes.push_back(std::make_pair(contig, size)); + } + + return ReferenceContigInfo(contigNamesAndSizes); +} + +} // namespace htshelpers +} diff --git a/common/HtsHelpers.hh b/ehunter/core/HtsHelpers.hh similarity index 77% rename from common/HtsHelpers.hh rename to ehunter/core/HtsHelpers.hh index a3b1619..a2b4456 100644 --- a/common/HtsHelpers.hh +++ b/ehunter/core/HtsHelpers.hh @@ -25,10 +25,11 @@ extern "C" { #include "htslib/hts.h" #include "htslib/sam.h" +#include "htslib/thread_pool.h" } -#include "common/ReferenceContigInfo.hh" -#include "reads/Read.hh" +#include "core/Read.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { @@ -36,10 +37,10 @@ namespace ehunter namespace htshelpers { - LinearAlignmentStats decodeAlignmentStats(bam1_t* htsAlignPtr); - bool isPrimaryAlignment(bam1_t* htsAlignPtr); - Read decodeRead(bam1_t* htsAlignPtr); - ReferenceContigInfo decodeContigInfo(bam_hdr_t* htsHeaderPtr); +LinearAlignmentStats decodeAlignmentStats(bam1_t* htsAlignPtr); +bool isPrimaryAlignment(bam1_t* htsAlignPtr); +Read decodeRead(bam1_t* htsAlignPtr); +ReferenceContigInfo decodeContigInfo(bam_hdr_t* htsHeaderPtr); } // namespace htshelpers diff --git a/stats/LocusStats.cpp b/ehunter/core/LocusStats.cpp similarity index 97% rename from stats/LocusStats.cpp rename to ehunter/core/LocusStats.cpp index 628a637..dcdd729 100644 --- a/stats/LocusStats.cpp +++ b/ehunter/core/LocusStats.cpp @@ -19,7 +19,7 @@ // // -#include "stats/LocusStats.hh" +#include "core/LocusStats.hh" #include #include @@ -61,6 +61,11 @@ void LocusStatsCalculator::inspect(const GraphAlignment& readAlign, const GraphA recordFragLen(readAlign, mateAlign); } +void LocusStatsCalculator::inspectRead(const GraphAlignment& readAlign) +{ + recordReadLen(readAlign); +} + static AlleleCount determineExpectedAlleleCount(ChromType chromType, Sex sex) { switch (chromType) diff --git a/stats/LocusStats.hh b/ehunter/core/LocusStats.hh similarity index 90% rename from stats/LocusStats.hh rename to ehunter/core/LocusStats.hh index 7be1474..8c777ad 100644 --- a/stats/LocusStats.hh +++ b/ehunter/core/LocusStats.hh @@ -31,9 +31,9 @@ #include "graphalign/GraphAlignment.hh" #include "graphcore/Graph.hh" -#include "common/Common.hh" -#include "common/GenomicRegion.hh" -#include "common/Reference.hh" +#include "core/Common.hh" +#include "core/GenomicRegion.hh" +#include "core/Reference.hh" namespace ehunter { @@ -41,7 +41,8 @@ namespace ehunter class LocusStats { public: - LocusStats(AlleleCount alleleCount, int meanReadLen, int medianFragLen, double depth) + LocusStats( + AlleleCount alleleCount = AlleleCount::kOne, int meanReadLen = 0, int medianFragLen = 0, double depth = 0) : alleleCount_(alleleCount) , meanReadLen_(meanReadLen) , medianFragLen_(medianFragLen) @@ -73,6 +74,7 @@ public: LocusStatsCalculator(ChromType chromType, const graphtools::Graph& graph); void inspect(const graphtools::GraphAlignment& readAlign, const graphtools::GraphAlignment& mateAlign); + void inspectRead(const graphtools::GraphAlignment& readAlign); LocusStats estimate(Sex sampleSex); void recordReadLen(const graphtools::GraphAlignment& readAlign); diff --git a/stats/LogSum.hh b/ehunter/core/LogSum.hh similarity index 100% rename from stats/LogSum.hh rename to ehunter/core/LogSum.hh diff --git a/common/Parameters.cpp b/ehunter/core/Parameters.cpp similarity index 95% rename from common/Parameters.cpp rename to ehunter/core/Parameters.cpp index 80808ee..ffaaa00 100644 --- a/common/Parameters.cpp +++ b/ehunter/core/Parameters.cpp @@ -19,4 +19,4 @@ // // -#include "common/Parameters.hh" +#include "core/Parameters.hh" diff --git a/common/Parameters.hh b/ehunter/core/Parameters.hh similarity index 77% rename from common/Parameters.hh rename to ehunter/core/Parameters.hh index 669ce37..1a7a4f4 100644 --- a/common/Parameters.hh +++ b/ehunter/core/Parameters.hh @@ -29,9 +29,10 @@ #include -#include "common/Common.hh" -#include "common/GenomicRegion.hh" -#include "common/ReferenceContigInfo.hh" +#include "alignment/SoftclippingAligner.hh" +#include "core/Common.hh" +#include "core/GenomicRegion.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { @@ -112,35 +113,41 @@ class HeuristicParameters { public: HeuristicParameters( - int regionExtensionLength, int qualityCutoffForGoodBaseCall, bool skipUnaligned, const std::string& alignerType, - int kmerLenForAlignment = 14, int paddingLength = 10, int seedAffixTrimLength = 14) + int regionExtensionLength, int qualityCutoffForGoodBaseCall, bool skipUnaligned, + const graphtools::AlignerType alignerType, int kmerLenForAlignment = 14, int paddingLength = 10, + int seedAffixTrimLength = 14, int orientationPredictorKmerLen = 10, int orientationPredictorMinKmerCount = 3) : regionExtensionLength_(regionExtensionLength) , qualityCutoffForGoodBaseCall_(qualityCutoffForGoodBaseCall) , skipUnaligned_(skipUnaligned) - , alignerType_(alignerType) + , alignerType_(std::move(alignerType)) , kmerLenForAlignment_(kmerLenForAlignment) , paddingLength_(paddingLength) , seedAffixTrimLength_(seedAffixTrimLength) - + , orientationPredictorKmerLen_(orientationPredictorKmerLen) + , orientationPredictorMinKmerCount_(orientationPredictorMinKmerCount) { } int regionExtensionLength() const { return regionExtensionLength_; } int qualityCutoffForGoodBaseCall() const { return qualityCutoffForGoodBaseCall_; } bool skipUnaligned() const { return skipUnaligned_; } - const std::string& alignerType() const { return alignerType_; } + graphtools::AlignerType alignerType() const { return alignerType_; } int kmerLenForAlignment() const { return kmerLenForAlignment_; } int paddingLength() const { return paddingLength_; } int seedAffixTrimLength() const { return seedAffixTrimLength_; } + int orientationPredictorKmerLen() const { return orientationPredictorKmerLen_; } + int orientationPredictorMinKmerCount() const { return orientationPredictorMinKmerCount_; } private: int regionExtensionLength_; int qualityCutoffForGoodBaseCall_; bool skipUnaligned_; - std::string alignerType_; + graphtools::AlignerType alignerType_; int kmerLenForAlignment_; int paddingLength_; int seedAffixTrimLength_; + int orientationPredictorKmerLen_; + int orientationPredictorMinKmerCount_; }; // Per-locus parameters (settable from variant catalog) controlling genotyping @@ -148,7 +155,7 @@ struct GenotyperParameters { // Base error rate assumed in SNV key-allele genotyping model double errorRate = 0.02; - // Threshold to call SNV key-allele confidently present / abstent + // Threshold to call SNV key-allele confidently present / absent double likelihoodRatioThreshold = 10000; // Minimal estimated locus coverage (depth) to attempt genotyping double minLocusCoverage = 10; @@ -161,8 +168,10 @@ class ProgramParameters public: ProgramParameters( InputPaths inputPaths, OutputPaths outputPaths, SampleParameters sample, HeuristicParameters heuristics, - AnalysisMode analysisMode, LogLevel logLevel) - : inputPaths_(std::move(inputPaths)) + AnalysisMode analysisMode, LogLevel logLevel, const int initThreadCount, const bool initDisableBamletOutput) + : threadCount(initThreadCount) + , disableBamletOutput(initDisableBamletOutput) + , inputPaths_(std::move(inputPaths)) , outputPaths_(std::move(outputPaths)) , sample_(std::move(sample)) , heuristics_(std::move(heuristics)) @@ -173,11 +182,14 @@ public: const InputPaths& inputPaths() const { return inputPaths_; } const OutputPaths& outputPaths() const { return outputPaths_; } - SampleParameters& sample() { return sample_; } + const SampleParameters& sample() const { return sample_; } const HeuristicParameters& heuristics() const { return heuristics_; } AnalysisMode analysisMode() const { return analysisMode_; } LogLevel logLevel() const { return logLevel_; } + int threadCount; + bool disableBamletOutput; + private: InputPaths inputPaths_; OutputPaths outputPaths_; diff --git a/reads/Read.cpp b/ehunter/core/Read.cpp similarity index 80% rename from reads/Read.cpp rename to ehunter/core/Read.cpp index e5e40f9..51e4172 100644 --- a/reads/Read.cpp +++ b/ehunter/core/Read.cpp @@ -21,7 +21,7 @@ // // -#include "reads/Read.hh" +#include "core/Read.hh" #include @@ -52,6 +52,16 @@ bool operator==(const LinearAlignmentStats& statsA, const LinearAlignmentStats& && mateMappingStatusesEqual); } +std::ostream& operator<<(std::ostream& out, const LinearAlignmentStats& alignmentStats) +{ + out << "tid: " << alignmentStats.chromId << " pos: " << alignmentStats.pos + << " mtid: " << alignmentStats.mateChromId << " mpos: " << alignmentStats.matePos + << " mapq: " << alignmentStats.mapq << "\n"; + out << "Paired/Mapped/MateMapped: " << alignmentStats.isPaired << "/" << alignmentStats.isMapped << "/" + << alignmentStats.isMateMapped << "\n"; + return out; +} + std::ostream& operator<<(std::ostream& out, const ReadId& readId) { out << readId.fragmentId() << "/" << static_cast(readId.mateNumber()); diff --git a/reads/Read.hh b/ehunter/core/Read.hh similarity index 96% rename from reads/Read.hh rename to ehunter/core/Read.hh index e5fef22..3697b24 100644 --- a/reads/Read.hh +++ b/ehunter/core/Read.hh @@ -29,7 +29,7 @@ #include -#include "classification/AlignmentClassifier.hh" +#include "alignment/AlignmentClassifier.hh" #include "graphalign/GraphAlignment.hh" #include "graphutils/SequenceOperations.hh" @@ -104,12 +104,12 @@ public: bool isFirstMate() const { return mateNumber() == MateNumber::kFirstMate; } bool isSecondMate() const { return mateNumber() == MateNumber::kSecondMate; } - // Return whether the read is reverse complemented relative to it's + // Return whether the read is reverse complemented relative to its // original direction during sequencing bool isReversed() const { return isReversed_; } - + void reverseComplement() - { + { sequence_ = graphtools::reverseComplement(sequence_); isReversed_ = !isReversed_; } @@ -132,6 +132,8 @@ struct LinearAlignmentStats bool isMateMapped = false; }; +std::ostream& operator<<(std::ostream& out, const LinearAlignmentStats& alignmentStats); + using ReadIdToLinearAlignmentStats = std::unordered_map; bool operator==(const Read& read, const Read& mate); diff --git a/reads/ReadPairs.cpp b/ehunter/core/ReadPairs.cpp similarity index 98% rename from reads/ReadPairs.cpp rename to ehunter/core/ReadPairs.cpp index f4f9c7a..fa7de05 100644 --- a/reads/ReadPairs.cpp +++ b/ehunter/core/ReadPairs.cpp @@ -19,7 +19,7 @@ // // -#include "reads/ReadPairs.hh" +#include "core/ReadPairs.hh" #include diff --git a/reads/ReadPairs.hh b/ehunter/core/ReadPairs.hh similarity index 98% rename from reads/ReadPairs.hh rename to ehunter/core/ReadPairs.hh index 8f71c66..5097edd 100644 --- a/reads/ReadPairs.hh +++ b/ehunter/core/ReadPairs.hh @@ -30,7 +30,7 @@ #include -#include "reads/Read.hh" +#include "core/Read.hh" namespace ehunter { diff --git a/stats/ReadSupportCalculator.cpp b/ehunter/core/ReadSupportCalculator.cpp similarity index 75% rename from stats/ReadSupportCalculator.cpp rename to ehunter/core/ReadSupportCalculator.cpp index bda6aef..488934c 100644 --- a/stats/ReadSupportCalculator.cpp +++ b/ehunter/core/ReadSupportCalculator.cpp @@ -20,7 +20,7 @@ // // -#include "stats/ReadSupportCalculator.hh" +#include "core/ReadSupportCalculator.hh" #include @@ -29,23 +29,23 @@ namespace ehunter namespace { - int countConsistentReads(const CountTable& table, int alleleSize) +int countConsistentReads(const CountTable& table, int alleleSize) +{ + int readCount = 0; + + for (const auto& sizeAndCount : table) { - int readCount = 0; + const int size = sizeAndCount.first; + const int count = sizeAndCount.second; - for (const auto& sizeAndCount : table) + if (size <= alleleSize) { - const int size = sizeAndCount.first; - const int count = sizeAndCount.second; - - if (size <= alleleSize) - { - readCount += count; - } + readCount += count; } - - return readCount; } + + return readCount; +} } int ReadSupportCalculator::getCountOfConsistentSpanningReads(int alleleSize) const diff --git a/stats/ReadSupportCalculator.hh b/ehunter/core/ReadSupportCalculator.hh similarity index 98% rename from stats/ReadSupportCalculator.hh rename to ehunter/core/ReadSupportCalculator.hh index 4c6050d..94b4755 100644 --- a/stats/ReadSupportCalculator.hh +++ b/ehunter/core/ReadSupportCalculator.hh @@ -24,7 +24,7 @@ #include -#include "common/CountTable.hh" +#include "core/CountTable.hh" namespace ehunter { diff --git a/common/Reference.cpp b/ehunter/core/Reference.cpp similarity index 99% rename from common/Reference.cpp rename to ehunter/core/Reference.cpp index 88936b7..c3109fb 100644 --- a/common/Reference.cpp +++ b/ehunter/core/Reference.cpp @@ -21,7 +21,7 @@ // // -#include "common/Reference.hh" +#include "core/Reference.hh" #include #include diff --git a/common/Reference.hh b/ehunter/core/Reference.hh similarity index 97% rename from common/Reference.hh rename to ehunter/core/Reference.hh index 44e7167..9b9791e 100644 --- a/common/Reference.hh +++ b/ehunter/core/Reference.hh @@ -32,8 +32,8 @@ // Include the fai class from samtools #include "htslib/faidx.h" -#include "common/GenomicRegion.hh" -#include "common/ReferenceContigInfo.hh" +#include "core/GenomicRegion.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { diff --git a/common/ReferenceContigInfo.cpp b/ehunter/core/ReferenceContigInfo.cpp similarity index 85% rename from common/ReferenceContigInfo.cpp rename to ehunter/core/ReferenceContigInfo.cpp index 0bf1670..6564d49 100644 --- a/common/ReferenceContigInfo.cpp +++ b/ehunter/core/ReferenceContigInfo.cpp @@ -19,7 +19,7 @@ // // -#include "common/ReferenceContigInfo.hh" +#include "core/ReferenceContigInfo.hh" #include #include @@ -34,19 +34,19 @@ namespace ehunter namespace { - // Removes "chr" prefix from contig names that contain it; adds it to contigs that don't - string generateAlternativeContigName(const string& originalName) +// Removes "chr" prefix from contig names that contain it; adds it to contigs that don't +string generateAlternativeContigName(const string& originalName) +{ + if (originalName.length() > 3 && originalName.substr(0, 3) == "chr") + { + return originalName.substr(3); + } + else { - if (originalName.length() > 3 && originalName.substr(0, 3) == "chr") - { - return originalName.substr(3); - } - else - { - return "chr" + originalName; - } + return "chr" + originalName; } } +} ReferenceContigInfo::ReferenceContigInfo(vector> namesAndSizes) : namesAndSizes_(std::move(namesAndSizes)) diff --git a/common/ReferenceContigInfo.hh b/ehunter/core/ReferenceContigInfo.hh similarity index 100% rename from common/ReferenceContigInfo.hh rename to ehunter/core/ReferenceContigInfo.hh diff --git a/ehunter/core/ThreadPool.hh b/ehunter/core/ThreadPool.hh new file mode 100644 index 0000000..109e331 --- /dev/null +++ b/ehunter/core/ThreadPool.hh @@ -0,0 +1,3 @@ +#pragma once + +#include "ctpl_stl.h" diff --git a/stats/WeightedPurityCalculator.cpp b/ehunter/core/WeightedPurityCalculator.cpp similarity index 56% rename from stats/WeightedPurityCalculator.cpp rename to ehunter/core/WeightedPurityCalculator.cpp index 619813b..00764d1 100644 --- a/stats/WeightedPurityCalculator.cpp +++ b/ehunter/core/WeightedPurityCalculator.cpp @@ -19,12 +19,12 @@ // // -#include "stats/WeightedPurityCalculator.hh" +#include "core/WeightedPurityCalculator.hh" #include +#include #include #include -#include #include @@ -38,61 +38,59 @@ namespace ehunter { namespace irrdetection { - using BaseCode = unsigned char; - // const int kMaxBaseCode = 15; - - // Core base codes - const BaseCode A = 0; - const BaseCode a = 1; - const BaseCode C = 2; - const BaseCode c = 3; - const BaseCode G = 4; - const BaseCode g = 5; - const BaseCode T = 6; - const BaseCode t = 7; - const BaseCode X = 8; - - // Degenerate base codes - const BaseCode B = 9; - const BaseCode D = 10; - const BaseCode H = 11; - const BaseCode K = 12; - const BaseCode M = 13; - const BaseCode N = 14; - const BaseCode R = 15; - const BaseCode S = 16; - const BaseCode V = 17; - const BaseCode W = 18; - const BaseCode Y = 19; - - const int kMaxQueryBaseCode = 8; - const int kMaxReferenceBaseCode = 19; - - const int maxBaseAscii = 255; - - const std::array kReferenceBaseEncodingTable - = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, A, B, C, D, X, X, G, H, X, X, K, X, M, N, X, X, X, R, S, T, X, V, W, X, Y, X, X, X, X, X, X, - X, A, X, C, X, X, X, G, X, X, X, X, X, X, X, X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; - - const std::array kQueryBaseEncodingTable - = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, A, X, C, X, X, X, G, X, X, X, X, X, X, X, X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, X, - X, a, X, c, X, X, X, g, X, X, X, X, X, X, X, X, X, X, X, X, t, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; - - const std::array, kMaxReferenceBaseCode + 1> - kReferenceQueryCodeScoreLookupTable = { - // clang-format off +using BaseCode = unsigned char; +// const int kMaxBaseCode = 15; + +// Core base codes +const BaseCode A = 0; +const BaseCode a = 1; +const BaseCode C = 2; +const BaseCode c = 3; +const BaseCode G = 4; +const BaseCode g = 5; +const BaseCode T = 6; +const BaseCode t = 7; +const BaseCode X = 8; + +// Degenerate base codes +const BaseCode B = 9; +const BaseCode D = 10; +const BaseCode H = 11; +const BaseCode K = 12; +const BaseCode M = 13; +const BaseCode N = 14; +const BaseCode R = 15; +const BaseCode S = 16; +const BaseCode V = 17; +const BaseCode W = 18; +const BaseCode Y = 19; + +const int kMaxQueryBaseCode = 8; +const int kMaxReferenceBaseCode = 19; + +const int maxBaseAscii = 255; + +const std::array kReferenceBaseEncodingTable + = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, A, B, C, D, X, X, G, H, X, + X, K, X, M, N, X, X, X, R, S, T, X, V, W, X, Y, X, X, X, X, X, X, X, A, X, C, X, X, X, G, X, X, X, X, X, X, X, + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; + +const std::array kQueryBaseEncodingTable + = { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, A, X, C, X, X, X, G, X, X, + X, X, X, X, X, X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, X, X, a, X, c, X, X, X, g, X, X, X, X, X, X, X, + X, X, X, X, X, t, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }; + +const std::array, kMaxReferenceBaseCode + 1> + kReferenceQueryCodeScoreLookupTable = { + // clang-format off // A a C c G g T t X {{ 1.0, 1.0, -1.0, 0.5, -1.0, 0.5, -1.0, 0.5, -1.0}, // A { 1.0, 1.0, -1.0, 0.5, -1.0, 0.5, -1.0, 0.5, -1.0}, // a @@ -114,14 +112,14 @@ namespace irrdetection { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 0.5, -1.0}, // V { 1.0, 1.0, -1.0, 0.5, -1.0, 0.5, 1.0, 1.0, -1.0}, // W {-1.0, 0.5, 1.0, 1.0, -1.0, 0.5, 1.0, 1.0, -1.0}} // Y - // clang-format on - }; + // clang-format on + }; - static inline double scoreBases(char referenceBase, char queryBase) - { - return kReferenceQueryCodeScoreLookupTable[kReferenceBaseEncodingTable[referenceBase]] - [kQueryBaseEncodingTable[queryBase]]; - } +static inline double scoreBases(char referenceBase, char queryBase) +{ + return kReferenceQueryCodeScoreLookupTable[kReferenceBaseEncodingTable[referenceBase]] + [kQueryBaseEncodingTable[queryBase]]; +} } WeightedPurityCalculator::WeightedPurityCalculator(const std::string& repeatUnit) diff --git a/stats/WeightedPurityCalculator.hh b/ehunter/core/WeightedPurityCalculator.hh similarity index 100% rename from stats/WeightedPurityCalculator.hh rename to ehunter/core/WeightedPurityCalculator.hh diff --git a/genotyping/AlignMatrix.cpp b/ehunter/genotyping/AlignMatrix.cpp similarity index 74% rename from genotyping/AlignMatrix.cpp rename to ehunter/genotyping/AlignMatrix.cpp index 2d068bd..6c40fb1 100644 --- a/genotyping/AlignMatrix.cpp +++ b/ehunter/genotyping/AlignMatrix.cpp @@ -24,6 +24,8 @@ #include #include +#include + using graphtools::GraphAlignment; using std::vector; @@ -119,29 +121,69 @@ std::ostream& operator<<(std::ostream& out, const AlignMatrix& matrix) { const auto& alignsByRead = matrix.matrix(); - for (const auto& aligns : alignsByRead) - { - for (const StrAlign& align : aligns) + const unsigned alignCount(alignsByRead.size()); + std::vector alignIndexes(alignCount); + std::iota(alignIndexes.begin(), alignIndexes.end(), 0); + + // Sort alignIndexes to canonicalize the alignmatrix output + std::sort( + alignIndexes.begin(), alignIndexes.end(), + [&alignsByRead](const unsigned ai, const unsigned bi) -> bool { - out << "("; - switch (align.type()) + const auto& a(alignsByRead[ai]); + const auto& b(alignsByRead[bi]); + if (a.size() < b.size()) + { + return true; + } + if (b.size() < a.size()) + { + return false; + } + for (unsigned i(0); i < a.size(); ++i) { - case StrAlign::Type::kOutside: - out << "O"; - break; - case StrAlign::Type::kInRepeat: - out << "I"; - break; - case StrAlign::Type::kSpanning: - out << "S"; - break; - case StrAlign::Type::kFlanking: - out << "F"; - break; + if (a[i] < b[i]) + { + return true; + } + if (b[i] < a[i]) + { + return false; + } } - out << "," << align.numMotifs() << "," << align.score() << "), "; + return false; + }); + + auto dumpStrAlign = [&out](const StrAlign& align) + { + out << "("; + switch (align.type()) + { + case StrAlign::Type::kOutside: + out << "O"; + break; + case StrAlign::Type::kInRepeat: + out << "I"; + break; + case StrAlign::Type::kSpanning: + out << "S"; + break; + case StrAlign::Type::kFlanking: + out << "F"; + break; + } + out << "," << align.numMotifs() << "," << align.score() << "), "; + }; + + for (unsigned origAlignIndex(0); origAlignIndex < alignCount; ++origAlignIndex) + { + const unsigned alignIndex(alignIndexes[origAlignIndex]); + const auto& aligns(alignsByRead[alignIndex]); + for (const StrAlign& align : aligns) + { + dumpStrAlign(align); } - out << std::endl; + out << "\n"; } return out; diff --git a/genotyping/AlignMatrix.hh b/ehunter/genotyping/AlignMatrix.hh similarity index 98% rename from genotyping/AlignMatrix.hh rename to ehunter/genotyping/AlignMatrix.hh index a015e9e..00056d1 100644 --- a/genotyping/AlignMatrix.hh +++ b/ehunter/genotyping/AlignMatrix.hh @@ -21,7 +21,8 @@ #pragma once -#include +#include +#include #include #include "graphalign/GraphAlignment.hh" diff --git a/genotyping/AlignMatrixFiltering.cpp b/ehunter/genotyping/AlignMatrixFiltering.cpp similarity index 100% rename from genotyping/AlignMatrixFiltering.cpp rename to ehunter/genotyping/AlignMatrixFiltering.cpp diff --git a/genotyping/AlignMatrixFiltering.hh b/ehunter/genotyping/AlignMatrixFiltering.hh similarity index 96% rename from genotyping/AlignMatrixFiltering.hh rename to ehunter/genotyping/AlignMatrixFiltering.hh index 994ef9e..a3956ea 100644 --- a/genotyping/AlignMatrixFiltering.hh +++ b/ehunter/genotyping/AlignMatrixFiltering.hh @@ -21,7 +21,7 @@ #pragma once -#include "common/CountTable.hh" +#include "core/CountTable.hh" #include "genotyping/AlignMatrix.hh" namespace ehunter diff --git a/genotyping/AlleleChecker.cpp b/ehunter/genotyping/AlleleChecker.cpp similarity index 80% rename from genotyping/AlleleChecker.cpp rename to ehunter/genotyping/AlleleChecker.cpp index 2a19a85..cbebc3d 100644 --- a/genotyping/AlleleChecker.cpp +++ b/ehunter/genotyping/AlleleChecker.cpp @@ -30,22 +30,19 @@ namespace ehunter namespace { - double poissonLogPmf(double lambda, double count) - { - return count * log(lambda) - lambda - boost::math::lgamma(count + 1); - } +double poissonLogPmf(double lambda, double count) +{ + return count * log(lambda) - lambda - boost::math::lgamma(count + 1); +} - double logBeta(int a, int b) - { - return boost::math::lgamma(a) + boost::math::lgamma(b) - boost::math::lgamma(a + b); - } +double logBeta(int a, int b) { return boost::math::lgamma(a) + boost::math::lgamma(b) - boost::math::lgamma(a + b); } - double logBinomCoef(int n, int k) { return -boost::math::log1p(n) - logBeta(n - k + 1, k + 1); } +double logBinomCoef(int n, int k) { return -boost::math::log1p(n) - logBeta(n - k + 1, k + 1); } - double binomLogPmf(int n, double p, int count) - { - return logBinomCoef(n, count) + count * log(p) + (n - count) * boost::math::log1p(-p); - } +double binomLogPmf(int n, double p, int count) +{ + return logBinomCoef(n, count) + count * log(p) + (n - count) * boost::math::log1p(-p); +} } diff --git a/genotyping/AlleleChecker.hh b/ehunter/genotyping/AlleleChecker.hh similarity index 98% rename from genotyping/AlleleChecker.hh rename to ehunter/genotyping/AlleleChecker.hh index 7dd9ecf..425ede6 100644 --- a/genotyping/AlleleChecker.hh +++ b/ehunter/genotyping/AlleleChecker.hh @@ -21,7 +21,7 @@ #include -#include "common/Common.hh" +#include "core/Common.hh" namespace ehunter { diff --git a/genotyping/FragLogliks.cpp b/ehunter/genotyping/FragLogliks.cpp similarity index 98% rename from genotyping/FragLogliks.cpp rename to ehunter/genotyping/FragLogliks.cpp index e126a55..f8e9deb 100644 --- a/genotyping/FragLogliks.cpp +++ b/ehunter/genotyping/FragLogliks.cpp @@ -32,7 +32,7 @@ #include #include -#include "thirdparty/spdlog/include/spdlog/spdlog.h" +#include "spdlog/spdlog.h" using std::pair; using std::vector; diff --git a/genotyping/FragLogliks.hh b/ehunter/genotyping/FragLogliks.hh similarity index 100% rename from genotyping/FragLogliks.hh rename to ehunter/genotyping/FragLogliks.hh diff --git a/genotyping/OneAlleleStrGenotyper.cpp b/ehunter/genotyping/OneAlleleStrGenotyper.cpp similarity index 99% rename from genotyping/OneAlleleStrGenotyper.cpp rename to ehunter/genotyping/OneAlleleStrGenotyper.cpp index f92a9ed..0ca6887 100644 --- a/genotyping/OneAlleleStrGenotyper.cpp +++ b/ehunter/genotyping/OneAlleleStrGenotyper.cpp @@ -26,7 +26,7 @@ #include #include -#include "stats/LogSum.hh" +#include "core/LogSum.hh" using std::unordered_set; @@ -179,4 +179,4 @@ double OneAlleleGenotyper::getAlleleLoglik(int motifCount) } } -} \ No newline at end of file +} diff --git a/genotyping/OneAlleleStrGenotyper.hh b/ehunter/genotyping/OneAlleleStrGenotyper.hh similarity index 100% rename from genotyping/OneAlleleStrGenotyper.hh rename to ehunter/genotyping/OneAlleleStrGenotyper.hh diff --git a/genotyping/RepeatGenotype.cpp b/ehunter/genotyping/RepeatGenotype.cpp similarity index 100% rename from genotyping/RepeatGenotype.cpp rename to ehunter/genotyping/RepeatGenotype.cpp diff --git a/genotyping/RepeatGenotype.hh b/ehunter/genotyping/RepeatGenotype.hh similarity index 99% rename from genotyping/RepeatGenotype.hh rename to ehunter/genotyping/RepeatGenotype.hh index 9b6f64e..210a46f 100644 --- a/genotyping/RepeatGenotype.hh +++ b/ehunter/genotyping/RepeatGenotype.hh @@ -29,7 +29,7 @@ #include #include -#include "common/Common.hh" +#include "core/Common.hh" namespace ehunter { diff --git a/genotyping/SmallVariantGenotype.cpp b/ehunter/genotyping/SmallVariantGenotype.cpp similarity index 100% rename from genotyping/SmallVariantGenotype.cpp rename to ehunter/genotyping/SmallVariantGenotype.cpp diff --git a/genotyping/SmallVariantGenotype.hh b/ehunter/genotyping/SmallVariantGenotype.hh similarity index 100% rename from genotyping/SmallVariantGenotype.hh rename to ehunter/genotyping/SmallVariantGenotype.hh diff --git a/genotyping/SmallVariantGenotyper.cpp b/ehunter/genotyping/SmallVariantGenotyper.cpp similarity index 84% rename from genotyping/SmallVariantGenotyper.cpp rename to ehunter/genotyping/SmallVariantGenotyper.cpp index f387555..809d046 100644 --- a/genotyping/SmallVariantGenotyper.cpp +++ b/ehunter/genotyping/SmallVariantGenotyper.cpp @@ -49,22 +49,28 @@ boost::optional SmallVariantGenotyper::genotype(int refCou const int totalReadCount = refCount + altCount; if (totalReadCount == 0) // missing genotype { - return optional(); + return {}; } - optional mostLikelyGenotype; - double bestLikelihood = -std::numeric_limits::max(); - for (const auto& currentGenotype : possibleGenotypes) + const unsigned genotypeCount(possibleGenotypes.size()); + if (genotypeCount == 0) { - const double currentLikelihood = genotypeLikelihood(currentGenotype, refCount, altCount); - if (currentLikelihood > bestLikelihood) + return {}; + } + + unsigned mostLikelyGenotypeIndex(0); + double bestLikelihood(0); + for (unsigned genotypeIndex(0); genotypeIndex < genotypeCount; ++genotypeIndex) + { + const double currentLikelihood = genotypeLikelihood(possibleGenotypes[genotypeIndex], refCount, altCount); + if ((genotypeIndex == 0) or (currentLikelihood > bestLikelihood)) { bestLikelihood = currentLikelihood; - mostLikelyGenotype = currentGenotype; + mostLikelyGenotypeIndex = genotypeIndex; } } - return mostLikelyGenotype; + return possibleGenotypes[mostLikelyGenotypeIndex]; } vector SmallVariantGenotyper::getPossibleGenotypes(int numAlleles) const diff --git a/genotyping/SmallVariantGenotyper.hh b/ehunter/genotyping/SmallVariantGenotyper.hh similarity index 98% rename from genotyping/SmallVariantGenotyper.hh rename to ehunter/genotyping/SmallVariantGenotyper.hh index c9e90e5..9f2fb25 100644 --- a/genotyping/SmallVariantGenotyper.hh +++ b/ehunter/genotyping/SmallVariantGenotyper.hh @@ -25,7 +25,7 @@ #include #include -#include "common/Common.hh" +#include "core/Common.hh" #include "genotyping/SmallVariantGenotype.hh" namespace ehunter diff --git a/genotyping/StrAlign.cpp b/ehunter/genotyping/StrAlign.cpp similarity index 97% rename from genotyping/StrAlign.cpp rename to ehunter/genotyping/StrAlign.cpp index db49500..ee329d4 100644 --- a/genotyping/StrAlign.cpp +++ b/ehunter/genotyping/StrAlign.cpp @@ -25,7 +25,7 @@ #include #include -#include "thirdparty/spdlog/include/spdlog/spdlog.h" +#include "spdlog/spdlog.h" #include "graphalign/LinearAlignment.hh" @@ -97,7 +97,7 @@ void scoreAlignment( } } -StrAlign ConsistentAlignmentCalculator::clipFromLeft(int numMotifsInAllele, const GraphAlignment& alignment) +StrAlign ConsistentAlignmentCalculator::clipFromLeft(int numMotifsInAllele, const GraphAlignment& alignment) const { int leftFlankScore = 0, strScore = 0, rightFlankScore = 0; int strIndelCount = 0; @@ -190,7 +190,7 @@ StrAlign ConsistentAlignmentCalculator::clipFromLeft(int numMotifsInAllele, cons } StrAlign -ConsistentAlignmentCalculator::clipFromRight(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) +ConsistentAlignmentCalculator::clipFromRight(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) const { int leftFlankScore = 0, strScore = 0, rightFlankScore = 0; int strIndelCount = 0; @@ -290,7 +290,7 @@ ConsistentAlignmentCalculator::clipFromRight(int numMotifsInAllele, const grapht return { StrAlign::Type::kOutside, 0, 0, 0 }; } -StrAlign ConsistentAlignmentCalculator::removeStutter(int numMotifsInAllele, const GraphAlignment& alignment) +StrAlign ConsistentAlignmentCalculator::removeStutter(int numMotifsInAllele, const GraphAlignment& alignment) const { int leftFlankScore = 0, strScore = 0, rightFlankScore = 0; int strIndelCount = 0; @@ -344,7 +344,8 @@ StrAlign ConsistentAlignmentCalculator::removeStutter(int numMotifsInAllele, con return { StrAlign::Type::kSpanning, numMotifsInAllele, alignmentScore, strIndelCount }; } -StrAlign ConsistentAlignmentCalculator::findConsistentAlignment(int numMotifsInAllele, const GraphAlignment& alignment) +StrAlign +ConsistentAlignmentCalculator::findConsistentAlignment(int numMotifsInAllele, const GraphAlignment& alignment) const { StrAlign stutterFreeAlign = removeStutter(numMotifsInAllele, alignment); StrAlign leftClipAlign = clipFromLeft(numMotifsInAllele, alignment); diff --git a/genotyping/StrAlign.hh b/ehunter/genotyping/StrAlign.hh similarity index 58% rename from genotyping/StrAlign.hh rename to ehunter/genotyping/StrAlign.hh index b3c1c5b..2029e02 100644 --- a/genotyping/StrAlign.hh +++ b/ehunter/genotyping/StrAlign.hh @@ -20,6 +20,7 @@ // #include +#include #include #include "graphalign/GraphAlignment.hh" @@ -30,7 +31,7 @@ namespace ehunter class StrAlign { public: - enum class Type + enum class Type : uint8_t { kSpanning, kFlanking, @@ -38,47 +39,72 @@ public: kOutside }; - StrAlign(Type type, int numMotifs, int score, int numIndels) - : type_(type) - , numMotifs_(numMotifs) - , score_(score) - , numIndels_(numIndels) - { - } - - StrAlign(char typeEncoding, int numMotifs, int score, int numIndels) - : type_(Type::kOutside) - , numMotifs_(numMotifs) - , score_(score) - , numIndels_(numIndels) + static Type decodeType(const char typeEncoding) { switch (typeEncoding) { case 'F': - type_ = Type::kFlanking; - break; + return Type::kFlanking; case 'S': - type_ = Type::kSpanning; - break; + return Type::kSpanning; case 'I': - type_ = Type::kInRepeat; - break; + return Type::kInRepeat; case 'O': - type_ = Type::kOutside; - break; + return Type::kOutside; default: - std::string message = "Encountered unknown StrAlign::Type: "; - message.push_back(typeEncoding); - throw std::runtime_error(message); + throw std::runtime_error("Encountered unknown StrAlign::Type: " + std::string(1, typeEncoding)); } } + StrAlign(Type type, int numMotifs, int score, int numIndels) + : type_(type) + , numIndels_(numIndels) + , numMotifs_(numMotifs) + , score_(score) + { + if ((numIndels < 0) or (numIndels > std::numeric_limits::max())) + { + throw std::runtime_error("numIndels out of range: " + std::to_string(numIndels)); + } + if ((numMotifs < 0) or (numMotifs > std::numeric_limits::max())) + { + throw std::runtime_error("numMotifs out of range: " + std::to_string(numMotifs)); + } + if ((score < std::numeric_limits::min()) + or (score > std::numeric_limits::max())) + { + throw std::runtime_error("score out of range: " + std::to_string(score)); + } + } + + StrAlign(char typeEncoding, int numMotifs, int score, int numIndels) + : StrAlign(decodeType(typeEncoding), numMotifs, score, numIndels) + { + } + bool operator==(const StrAlign& other) const { return type_ == other.type_ && numMotifs_ == other.numMotifs_ && score_ == other.score_ && numIndels_ == other.numIndels_; } + bool operator<(const StrAlign& other) const + { + if (type_ < other.type_) + return true; + if (type_ > other.type_) + return false; + if (score_ < other.score_) + return true; + if (score_ > other.score_) + return false; + if (numMotifs_ < other.numMotifs_) + return true; + if (numMotifs_ > other.numMotifs_) + return false; + return (numIndels_ < other.numIndels_); + } + Type type() const { return type_; } int numMotifs() const { return numMotifs_; } int score() const { return score_; } @@ -86,9 +112,9 @@ public: private: Type type_; - int numMotifs_; - int score_; - int numIndels_; + uint8_t numIndels_; + uint16_t numMotifs_; + int16_t score_; }; std::ostream& operator<<(std::ostream& out, StrAlign::Type type); @@ -105,13 +131,13 @@ public: int strNodeId() const { return strNodeId_; } // Calculates longest consistent alignment by clipping from left (right) - StrAlign clipFromLeft(int numMotifsInAllele, const graphtools::GraphAlignment& alignment); - StrAlign clipFromRight(int numMotifsInAllele, const graphtools::GraphAlignment& alignment); + StrAlign clipFromLeft(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) const; + StrAlign clipFromRight(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) const; // Calculates consistent alignment by removing PCR stutter - StrAlign removeStutter(int numMotifsInAllele, const graphtools::GraphAlignment& alignment); + StrAlign removeStutter(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) const; - StrAlign findConsistentAlignment(int numMotifsInAllele, const graphtools::GraphAlignment& alignment); + StrAlign findConsistentAlignment(int numMotifsInAllele, const graphtools::GraphAlignment& alignment) const; private: int matchScore_ = 5; diff --git a/genotyping/StrGenotyper.cpp b/ehunter/genotyping/StrGenotyper.cpp similarity index 100% rename from genotyping/StrGenotyper.cpp rename to ehunter/genotyping/StrGenotyper.cpp diff --git a/genotyping/StrGenotyper.hh b/ehunter/genotyping/StrGenotyper.hh similarity index 97% rename from genotyping/StrGenotyper.hh rename to ehunter/genotyping/StrGenotyper.hh index 4cb361c..7f2d10a 100644 --- a/genotyping/StrGenotyper.hh +++ b/ehunter/genotyping/StrGenotyper.hh @@ -23,7 +23,7 @@ #include -#include "common/Common.hh" +#include "core/Common.hh" #include "genotyping/AlignMatrix.hh" #include "genotyping/RepeatGenotype.hh" diff --git a/genotyping/TwoAlleleStrGenotyper.cpp b/ehunter/genotyping/TwoAlleleStrGenotyper.cpp similarity index 99% rename from genotyping/TwoAlleleStrGenotyper.cpp rename to ehunter/genotyping/TwoAlleleStrGenotyper.cpp index d09a8e9..5741f97 100644 --- a/genotyping/TwoAlleleStrGenotyper.cpp +++ b/ehunter/genotyping/TwoAlleleStrGenotyper.cpp @@ -25,7 +25,7 @@ #include -#include "stats/LogSum.hh" +#include "core/LogSum.hh" using std::unordered_set; diff --git a/genotyping/TwoAlleleStrGenotyper.hh b/ehunter/genotyping/TwoAlleleStrGenotyper.hh similarity index 100% rename from genotyping/TwoAlleleStrGenotyper.hh rename to ehunter/genotyping/TwoAlleleStrGenotyper.hh diff --git a/output/BamletWriter.cpp b/ehunter/io/BamletWriter.cpp similarity index 89% rename from output/BamletWriter.cpp rename to ehunter/io/BamletWriter.cpp index 7f19359..4ffdfef 100644 --- a/output/BamletWriter.cpp +++ b/ehunter/io/BamletWriter.cpp @@ -20,7 +20,7 @@ // // -#include "output/BamletWriter.hh" +#include "io/BamletWriter.hh" #include @@ -68,17 +68,22 @@ BamletWriter::BamletWriter( : filePtr_(hts_open(bamletPath.c_str(), "wb"), hts_close) , bamHeader_(bam_hdr_init(), bam_hdr_destroy) , contigInfo_(contigInfo) + , writeThread_(&BamletWriter::writeHtsAlignments, this) { - for (const auto& locusIdAndSpec : regionCatalog) + for (const auto& locusSpec : regionCatalog) { - const auto& locusId = locusIdAndSpec.first; - const auto& locusSpec = locusIdAndSpec.second; - graphReferenceMappings_.emplace(std::make_pair(locusId, generateMapping(contigInfo, locusSpec))); + graphReferenceMappings_.emplace(locusSpec.locusId(), generateMapping(contigInfo, locusSpec)); } writeHeader(); } +BamletWriter::~BamletWriter() +{ + writeQueue_.push(nullptr); + writeThread_.join(); +} + void BamletWriter::writeHeader() { const string initHeader = "@HD\tVN:1.4\tSO:unknown\n"; @@ -105,8 +110,8 @@ void BamletWriter::writeHeader() } void BamletWriter::write( - const string& locusId, const string& fragmentName, const string& query, bool isFirstMate, - bool isReversed, bool isMateReversed, const GraphAlignment& alignment) + const string& locusId, const string& fragmentName, const string& query, bool isFirstMate, bool isReversed, + bool isMateReversed, const GraphAlignment& alignment) { const GraphReferenceMapping& referenceMapping = graphReferenceMappings_.at(locusId); auto optionalReferenceInterval = referenceMapping.map(alignment.path()); @@ -176,7 +181,7 @@ void BamletWriter::write( htsAlignmentPtr->core.flag = BAM_FUNMAP; htsAlignmentPtr->core.flag += BAM_FPAIRED + BAM_FMUNMAP; - + if (isReversed) htsAlignmentPtr->core.flag += BAM_FREVERSE; if (isMateReversed) @@ -220,12 +225,23 @@ void BamletWriter::write( htsAlignmentPtr, graphAlignmentBamTag.c_str(), 'Z', alignmentEncoding.length() + 1, reinterpret_cast(&alignmentEncoding[0])); - if (bam_write1(filePtr_->fp.bgzf, htsAlignmentPtr) == 0) + writeQueue_.push(htsAlignmentPtr); +} + +void BamletWriter::writeHtsAlignments() +{ + bam1_t* htsAlignmentPtr(nullptr); + while (true) { - throw std::logic_error("Cannot write alignment"); + writeQueue_.pop(htsAlignmentPtr); + if (not htsAlignmentPtr) + return; + if (bam_write1(filePtr_->fp.bgzf, htsAlignmentPtr) == 0) + { + throw std::logic_error("Cannot write alignment"); + } + bam_destroy1(htsAlignmentPtr); } - - bam_destroy1(htsAlignmentPtr); } } diff --git a/output/BamletWriter.hh b/ehunter/io/BamletWriter.hh similarity index 69% rename from output/BamletWriter.hh rename to ehunter/io/BamletWriter.hh index 754b36b..a510033 100644 --- a/output/BamletWriter.hh +++ b/ehunter/io/BamletWriter.hh @@ -4,7 +4,8 @@ // All rights reserved. // // Author: Felix Schlesinger , -// Egor Dolzhenko +// Egor Dolzhenko , +// Chris Saunders // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,46 +25,61 @@ #include #include +#include #include +#include "boost/noncopyable.hpp" // cppcheck-suppress missingInclude #include "htslib/hts.h" // cppcheck-suppress missingInclude #include "htslib/sam.h" +#include "core/ConcurrentQueue.hh" +#include "core/Read.hh" +#include "core/ReferenceContigInfo.hh" #include "graphalign/GraphAlignment.hh" #include "graphcore/GraphReferenceMapping.hh" #include "graphio/AlignmentWriter.hh" - -#include "common/ReferenceContigInfo.hh" -#include "reads/Read.hh" -#include "region_spec/LocusSpecification.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { -class BamletWriter : public graphtools::AlignmentWriter +/// Supports multiple threads calling the write() method on the same object. To make this more efficient for high +/// thread counts, this object creates its own asynchronous write thread to prevent calling threads from blocking on +/// on the final bam record write operation. +/// +class BamletWriter : public graphtools::AlignmentWriter, private boost::noncopyable { public: BamletWriter( const std::string& bamletPath, const ReferenceContigInfo& contigInfo, const RegionCatalog& regionCatalog); - ~BamletWriter() override = default; + ~BamletWriter() override; + /// Thread safe void write( const std::string& locusId, const std::string& fragmentName, const std::string& query, bool isFirstMate, bool isReversed, bool isMateReversed, const graphtools::GraphAlignment& alignment) override; private: void writeHeader(); + + /// Thread safe void write( const graphtools::ReferenceInterval& interval, const std::string& fragmentName, const std::string& query, bool isFirstMate, bool isReversed, bool isMateReversed, const graphtools::GraphAlignment& alignment); + /// Function executed by dedicated bam writer thread + void writeHtsAlignments(); + std::unique_ptr filePtr_; std::unique_ptr bamHeader_; ReferenceContigInfo contigInfo_; std::unordered_map graphReferenceMappings_; + + ConcurrentQueue writeQueue_; + std::thread writeThread_; }; } diff --git a/input/CatalogLoading.cpp b/ehunter/io/CatalogLoading.cpp similarity index 96% rename from input/CatalogLoading.cpp rename to ehunter/io/CatalogLoading.cpp index fbffd1e..c4c609f 100644 --- a/input/CatalogLoading.cpp +++ b/ehunter/io/CatalogLoading.cpp @@ -20,7 +20,7 @@ // // -#include "input/CatalogLoading.hh" +#include "io/CatalogLoading.hh" #include #include @@ -33,12 +33,12 @@ #include +#include "spdlog/spdlog.h" #include "thirdparty/json/json.hpp" -#include "thirdparty/spdlog/include/spdlog/spdlog.h" -#include "common/Common.hh" -#include "common/Reference.hh" -#include "input/LocusSpecDecoding.hh" +#include "core/Common.hh" +#include "core/Reference.hh" +#include "io/LocusSpecDecoding.hh" using boost::optional; using graphtools::NodeId; @@ -217,11 +217,12 @@ RegionCatalog loadLocusCatalogFromDisk( makeArray(catalogJson); RegionCatalog catalog; + catalog.reserve(catalogJson.size()); for (auto& locusJson : catalogJson) { LocusDescriptionFromUser userDescription = loadUserDescription(locusJson, reference.contigInfo()); LocusSpecification locusSpec = decodeLocusSpecification(userDescription, reference, heuristicParams); - catalog.emplace(std::make_pair(locusSpec.locusId(), locusSpec)); + catalog.emplace_back(std::move(locusSpec)); } return catalog; diff --git a/input/CatalogLoading.hh b/ehunter/io/CatalogLoading.hh similarity index 87% rename from input/CatalogLoading.hh rename to ehunter/io/CatalogLoading.hh index d07584e..6670cbb 100644 --- a/input/CatalogLoading.hh +++ b/ehunter/io/CatalogLoading.hh @@ -24,10 +24,10 @@ #include -#include "common/Common.hh" -#include "common/Parameters.hh" -#include "common/Reference.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Common.hh" +#include "core/Parameters.hh" +#include "core/Reference.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { diff --git a/input/GraphBlueprint.cpp b/ehunter/io/GraphBlueprint.cpp similarity index 99% rename from input/GraphBlueprint.cpp rename to ehunter/io/GraphBlueprint.cpp index e158b7d..4f95d75 100644 --- a/input/GraphBlueprint.cpp +++ b/ehunter/io/GraphBlueprint.cpp @@ -19,7 +19,7 @@ // // -#include "input/GraphBlueprint.hh" +#include "io/GraphBlueprint.hh" #include #include diff --git a/input/GraphBlueprint.hh b/ehunter/io/GraphBlueprint.hh similarity index 100% rename from input/GraphBlueprint.hh rename to ehunter/io/GraphBlueprint.hh diff --git a/output/JsonWriter.cpp b/ehunter/io/JsonWriter.cpp similarity index 92% rename from output/JsonWriter.cpp rename to ehunter/io/JsonWriter.cpp index 90e1ae8..b728f2b 100644 --- a/output/JsonWriter.cpp +++ b/ehunter/io/JsonWriter.cpp @@ -19,7 +19,7 @@ // // -#include "output/JsonWriter.hh" +#include "io/JsonWriter.hh" #include #include @@ -28,8 +28,8 @@ #include #include -#include "common/Common.hh" -#include "stats/ReadSupportCalculator.hh" +#include "core/Common.hh" +#include "core/ReadSupportCalculator.hh" namespace ehunter { @@ -64,11 +64,12 @@ void JsonWriter::write(std::ostream& out) sampleParametersRecord["Sex"] = streamToString(sampleParams_.sex()); Json resultsRecord; - for (const auto& locusIdAndFindings : sampleFindings_) + const unsigned locusCount(sampleFindings_.size()); + for (unsigned locusIndex(0); locusIndex < locusCount; ++locusIndex) { - const string& locusId = locusIdAndFindings.first; - const LocusSpecification& locusSpec = regionCatalog_.at(locusId); - const LocusFindings& locusFindings = locusIdAndFindings.second; + const LocusSpecification& locusSpec = regionCatalog_[locusIndex]; + const LocusFindings& locusFindings = sampleFindings_[locusIndex]; + const std::string& locusId(locusSpec.locusId()); Json locusRecord; locusRecord["LocusId"] = locusId; diff --git a/output/JsonWriter.hh b/ehunter/io/JsonWriter.hh similarity index 94% rename from output/JsonWriter.hh rename to ehunter/io/JsonWriter.hh index 543e10f..2864366 100644 --- a/output/JsonWriter.hh +++ b/ehunter/io/JsonWriter.hh @@ -21,9 +21,9 @@ #pragma once -#include "common/Parameters.hh" -#include "region_analysis/LocusFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Parameters.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" #include "thirdparty/json/json.hpp" diff --git a/input/LocusSpecDecoding.cpp b/ehunter/io/LocusSpecDecoding.cpp similarity index 97% rename from input/LocusSpecDecoding.cpp rename to ehunter/io/LocusSpecDecoding.cpp index 25aa301..f82db27 100644 --- a/input/LocusSpecDecoding.cpp +++ b/ehunter/io/LocusSpecDecoding.cpp @@ -19,7 +19,7 @@ // // -#include "input/LocusSpecDecoding.hh" +#include "io/LocusSpecDecoding.hh" #include #include @@ -27,8 +27,8 @@ #include -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using boost::optional; using graphtools::Graph; @@ -299,8 +299,8 @@ LocusSpecification decodeLocusSpecification( } LocusSpecification locusSpec( - userDescription.locusId, chromType, targetReadExtractionRegions, locusGraph, referenceRegionsOfGraphNodes, - parameters); + userDescription.locusId, chromType, std::move(targetReadExtractionRegions), std::move(locusGraph), + std::move(referenceRegionsOfGraphNodes), std::move(parameters)); locusSpec.setOfftargetReadExtractionRegions(userDescription.offtargetRegions); int variantIndex = 0; diff --git a/input/LocusSpecDecoding.hh b/ehunter/io/LocusSpecDecoding.hh similarity index 91% rename from input/LocusSpecDecoding.hh rename to ehunter/io/LocusSpecDecoding.hh index a83a8e4..896a01b 100644 --- a/input/LocusSpecDecoding.hh +++ b/ehunter/io/LocusSpecDecoding.hh @@ -26,10 +26,10 @@ #include "boost/optional.hpp" -#include "common/GenomicRegion.hh" -#include "common/Parameters.hh" -#include "common/Reference.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/GenomicRegion.hh" +#include "core/Parameters.hh" +#include "core/Reference.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { diff --git a/input/ParameterLoading.cpp b/ehunter/io/ParameterLoading.cpp similarity index 79% rename from input/ParameterLoading.cpp rename to ehunter/io/ParameterLoading.cpp index f0ae978..e79ccf8 100644 --- a/input/ParameterLoading.cpp +++ b/ehunter/io/ParameterLoading.cpp @@ -19,7 +19,7 @@ // // -#include "input/ParameterLoading.hh" +#include "io/ParameterLoading.hh" #include #include @@ -28,8 +28,8 @@ #include #include -#include "input/SampleStats.hh" -#include "src/Version.hh" +#include "app/Version.hh" +#include "io/SampleStats.hh" namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -58,11 +58,13 @@ struct UserParameters // Heuristic parameters string alignerType; int regionExtensionLength; - int qualityCutoffForGoodBaseCall; + int qualityCutoffForGoodBaseCall = 20; bool skipUnaligned; string analysisMode; string logLevel; + int threadCount; + bool disableBamletOutput = false; }; boost::optional tryParsingUserParameters(int argc, char** argv) @@ -87,34 +89,44 @@ boost::optional tryParsingUserParameters(int argc, char** argv) po::options_description advancedOptions("Advanced options"); advancedOptions.add_options() ("aligner,a", po::value(¶ms.alignerType)->default_value("dag-aligner"), "Specify which aligner to use (dag-aligner or path-aligner)") - ("analysis-mode,m", po::value(¶ms.analysisMode)->default_value("seeking"), "Specify which analysis workflow to use (seeking or streaming)"); + ("analysis-mode,m", po::value(¶ms.analysisMode)->default_value("seeking"), "Specify which analysis workflow to use (seeking or streaming)") + ("threads,n", po::value(¶ms.threadCount)->default_value(1), "Number of threads to use") + ; // clang-format on - po::options_description cmdlineOptions; - cmdlineOptions.add(basicOptions).add(advancedOptions); + // Internal options are added for development and debug activities, these are suppressed from the commandline + // because they may be removed or changed without deprecation. - if (argc == 1) - { - std::cerr << cmdlineOptions << std::endl; - return boost::optional(); - } + // clang-format off + po::options_description internalOptions("Internal options (not stable in future releases)"); + internalOptions.add_options() + ("disable-bamlet-output", "Disable bamlet output") + ; + // clang-format on + + po::options_description visibleOptions; + visibleOptions.add(basicOptions).add(advancedOptions); + + po::options_description cmdlineOptions(visibleOptions); + cmdlineOptions.add(internalOptions); po::variables_map argumentMap; po::store(po::command_line_parser(argc, argv).options(cmdlineOptions).run(), argumentMap); - if (argumentMap.count("help")) + if ((argc == 1) or argumentMap.count("help")) { - std::cerr << basicOptions << std::endl; - std::cerr << advancedOptions << std::endl; - return boost::optional(); + std::cerr << visibleOptions << std::endl; + return {}; } if (argumentMap.count("version")) { std::cerr << "Starting " << kProgramVersion << std::endl; - return boost::optional(); + return {}; } + params.disableBamletOutput = argumentMap.count("disable-bamlet-output"); + po::notify(argumentMap); return params; @@ -163,9 +175,18 @@ static void assertIndexExists(const string& htsFilePath) void assertValidity(const UserParameters& userParameters) { + // Validate analysis Mode: + if ((userParameters.analysisMode != "seeking") and (userParameters.analysisMode != "streaming")) + { + throw std::invalid_argument(userParameters.analysisMode + " is not a valid analysis mode"); + } + // Validate input file paths assertPathToExistingFile(userParameters.htsFilePath); - assertIndexExists(userParameters.htsFilePath); + if (userParameters.analysisMode != "streaming") + { + assertIndexExists(userParameters.htsFilePath); + } assertPathToExistingFile(userParameters.referencePath); assertPathToExistingFile(userParameters.catalogPath); @@ -198,13 +219,19 @@ void assertValidity(const UserParameters& userParameters) const int kMinQualityCutoffForGoodBaseCall = 5; const int kMaxQualityCutoffForGoodBaseCall = 40; if (userParameters.qualityCutoffForGoodBaseCall < kMinQualityCutoffForGoodBaseCall - && userParameters.qualityCutoffForGoodBaseCall > kMaxQualityCutoffForGoodBaseCall) + || userParameters.qualityCutoffForGoodBaseCall > kMaxQualityCutoffForGoodBaseCall) { const string message = "Base call quality cutoff of " + to_string(userParameters.qualityCutoffForGoodBaseCall) + " is not supported; the range of allowed cutoffs is between " + to_string(kMinQualityCutoffForGoodBaseCall) + " and " + to_string(kMaxQualityCutoffForGoodBaseCall); throw std::invalid_argument(message); } + + if (userParameters.threadCount < 1) + { + const string message = "Thread count cannot be less than 1"; + throw std::invalid_argument(message); + } } SampleParameters decodeSampleParameters(const UserParameters& userParams) @@ -259,6 +286,22 @@ LogLevel decodeLogLevel(const string& encoding) } } +static graphtools::AlignerType decodeAlignerType(const string& alignerType) +{ + if (alignerType == "path-aligner") + { + return graphtools::AlignerType::PATH_ALIGNER; + } + if (alignerType == "dag-aligner") + { + return graphtools::AlignerType::DAG_ALIGNER; + } + else + { + throw std::logic_error(alignerType + " is not a valid aligner type"); + } +} + boost::optional tryLoadingProgramParameters(int argc, char** argv) { auto optionalUserParameters = tryParsingUserParameters(argc, argv); @@ -278,7 +321,7 @@ boost::optional tryLoadingProgramParameters(int argc, char** SampleParameters sampleParameters = decodeSampleParameters(userParams); HeuristicParameters heuristicParameters( userParams.regionExtensionLength, userParams.qualityCutoffForGoodBaseCall, userParams.skipUnaligned, - userParams.alignerType); + decodeAlignerType(userParams.alignerType)); LogLevel logLevel; try @@ -302,7 +345,9 @@ boost::optional tryLoadingProgramParameters(int argc, char** throw std::invalid_argument(message); } - return ProgramParameters(inputPaths, outputPaths, sampleParameters, heuristicParameters, analysisMode, logLevel); + return ProgramParameters( + inputPaths, outputPaths, sampleParameters, heuristicParameters, analysisMode, logLevel, userParams.threadCount, + userParams.disableBamletOutput); } } diff --git a/input/ParameterLoading.hh b/ehunter/io/ParameterLoading.hh similarity index 96% rename from input/ParameterLoading.hh rename to ehunter/io/ParameterLoading.hh index d8e5b85..796917d 100644 --- a/input/ParameterLoading.hh +++ b/ehunter/io/ParameterLoading.hh @@ -23,7 +23,7 @@ #include -#include "common/Parameters.hh" +#include "core/Parameters.hh" namespace ehunter { diff --git a/input/RegionGraph.cpp b/ehunter/io/RegionGraph.cpp similarity index 98% rename from input/RegionGraph.cpp rename to ehunter/io/RegionGraph.cpp index b490287..2e44571 100644 --- a/input/RegionGraph.cpp +++ b/ehunter/io/RegionGraph.cpp @@ -19,14 +19,14 @@ // // -#include "input/RegionGraph.hh" +#include "io/RegionGraph.hh" #include #include #include #include -#include "input/GraphBlueprint.hh" +#include "io/GraphBlueprint.hh" using graphtools::Graph; using graphtools::NodeId; diff --git a/input/RegionGraph.hh b/ehunter/io/RegionGraph.hh similarity index 96% rename from input/RegionGraph.hh rename to ehunter/io/RegionGraph.hh index 1125517..22597a6 100644 --- a/input/RegionGraph.hh +++ b/ehunter/io/RegionGraph.hh @@ -25,7 +25,7 @@ #include "graphcore/Graph.hh" -#include "input/GraphBlueprint.hh" +#include "io/GraphBlueprint.hh" namespace ehunter { diff --git a/input/SampleStats.cpp b/ehunter/io/SampleStats.cpp similarity index 87% rename from input/SampleStats.cpp rename to ehunter/io/SampleStats.cpp index 03161ec..342ee75 100644 --- a/input/SampleStats.cpp +++ b/ehunter/io/SampleStats.cpp @@ -20,7 +20,7 @@ // // -#include "input/SampleStats.hh" +#include "io/SampleStats.hh" #include #include @@ -31,7 +31,7 @@ #include "htslib/hts.h" #include "htslib/sam.h" -#include "common/HtsHelpers.hh" +#include "core/HtsHelpers.hh" using std::pair; using std::string; @@ -88,19 +88,20 @@ int extractReadLength(const string& htsFilePath) ReferenceContigInfo extractReferenceContigInfo(const std::string& htsFilePath) { - samFile* htsFilePtr = sam_open(htsFilePath.c_str(), "r"); + std::unique_ptr htsFilePtr(hts_open(htsFilePath.c_str(), "r"), hts_close); if (!htsFilePtr) { throw std::runtime_error("Failed to read " + htsFilePath); } - bam_hdr_t* htsHeaderPtr = sam_hdr_read(htsFilePtr); + std::unique_ptr htsHeaderPtr( + sam_hdr_read(htsFilePtr.get()), bam_hdr_destroy); if (!htsHeaderPtr) { throw std::runtime_error("Failed to read the header of " + htsFilePath); } - return htshelpers::decodeContigInfo(htsHeaderPtr); + return htshelpers::decodeContigInfo(htsHeaderPtr.get()); } } diff --git a/input/SampleStats.hh b/ehunter/io/SampleStats.hh similarity index 96% rename from input/SampleStats.hh rename to ehunter/io/SampleStats.hh index 794f7d9..610ba4b 100644 --- a/input/SampleStats.hh +++ b/ehunter/io/SampleStats.hh @@ -24,7 +24,7 @@ #include -#include "common/ReferenceContigInfo.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { diff --git a/output/VcfHeader.cpp b/ehunter/io/VcfHeader.cpp similarity index 96% rename from output/VcfHeader.cpp rename to ehunter/io/VcfHeader.cpp index d937494..506b33d 100644 --- a/output/VcfHeader.cpp +++ b/ehunter/io/VcfHeader.cpp @@ -19,7 +19,7 @@ // // -#include "output/VcfHeader.hh" +#include "io/VcfHeader.hh" #include @@ -149,11 +149,11 @@ void outputVcfHeader(const RegionCatalog& locusCatalog, const SampleFindings& sa FieldDescriptionCatalog fieldDescriptionCatalog; - for (const auto& locusIdAndFindings : sampleFindings) + const unsigned locusCount(sampleFindings.size()); + for (unsigned locusIndex(0); locusIndex < locusCount; ++locusIndex) { - const string& locusId = locusIdAndFindings.first; - const LocusSpecification& locusSpec = locusCatalog.at(locusId); - const LocusFindings& locusFindings = locusIdAndFindings.second; + const LocusSpecification& locusSpec = locusCatalog[locusIndex]; + const LocusFindings& locusFindings = sampleFindings[locusIndex]; for (const auto& variantIdAndFindings : locusFindings.findingsForEachVariant) { diff --git a/output/VcfHeader.hh b/ehunter/io/VcfHeader.hh similarity index 96% rename from output/VcfHeader.hh rename to ehunter/io/VcfHeader.hh index 7ea66af..b87cea9 100644 --- a/output/VcfHeader.hh +++ b/ehunter/io/VcfHeader.hh @@ -25,8 +25,8 @@ #include #include -#include "region_analysis/LocusFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { diff --git a/output/VcfWriter.cpp b/ehunter/io/VcfWriter.cpp similarity index 93% rename from output/VcfWriter.cpp rename to ehunter/io/VcfWriter.cpp index ef10636..a90999b 100644 --- a/output/VcfWriter.cpp +++ b/ehunter/io/VcfWriter.cpp @@ -19,7 +19,7 @@ // // -#include "output/VcfWriter.hh" +#include "io/VcfWriter.hh" #include #include @@ -29,9 +29,9 @@ #include -#include "output/VcfHeader.hh" -#include "output/VcfWriterHelpers.hh" -#include "stats/ReadSupportCalculator.hh" +#include "core/ReadSupportCalculator.hh" +#include "io/VcfHeader.hh" +#include "io/VcfWriterHelpers.hh" using boost::optional; using std::deque; @@ -89,13 +89,13 @@ VcfWriter::VcfWriter( void VcfWriter::writeBody(ostream& out) { - const std::vector& ids = VcfWriter::getSortedIdPairs(); + const std::vector& ids = VcfWriter::getSortedIdPairs(); for (const auto& pair : ids) { - const string& locusId = pair.first; - const LocusSpecification& locusSpec = regionCatalog_.at(locusId); - const LocusFindings& locusFindings = sampleFindings_.at(locusId); + const unsigned locusIndex = pair.first; + const LocusSpecification& locusSpec = regionCatalog_[locusIndex]; + const LocusFindings& locusFindings = sampleFindings_[locusIndex]; const string& variantId = pair.second; const VariantSpecification& variantSpec = locusSpec.getVariantSpecById(variantId); @@ -107,16 +107,16 @@ void VcfWriter::writeBody(ostream& out) } } -const std::vector VcfWriter::getSortedIdPairs() +const std::vector VcfWriter::getSortedIdPairs() { - using VariantTuple = std::tuple; + using VariantTuple = std::tuple; std::vector tuples; - for (const auto& locusIdAndFindings : sampleFindings_) + const unsigned locusCount(sampleFindings_.size()); + for (unsigned locusIndex(0); locusIndex < locusCount; ++locusIndex) { - const string& locusId = locusIdAndFindings.first; - const LocusSpecification& locusSpec = regionCatalog_.at(locusId); - const LocusFindings& locusFindings = locusIdAndFindings.second; + const LocusSpecification& locusSpec = regionCatalog_[locusIndex]; + const LocusFindings& locusFindings = sampleFindings_[locusIndex]; for (const auto& variantIdAndFindings : locusFindings.findingsForEachVariant) { @@ -124,12 +124,12 @@ const std::vector VcfWriter::getSortedIdPairs() const VariantSpecification& variantSpec = locusSpec.getVariantSpecById(variantId); tuples.emplace_back( variantSpec.referenceLocus().contigIndex(), variantSpec.referenceLocus().start(), - variantSpec.referenceLocus().end(), LocusIdAndVariantId(locusId, variantId)); + variantSpec.referenceLocus().end(), LocusIndexAndVariantId(locusIndex, variantId)); } } std::sort(tuples.begin(), tuples.end()); - std::vector idPairs; + std::vector idPairs; for (const auto& t : tuples) { idPairs.emplace_back(std::get<3>(t)); diff --git a/output/VcfWriter.hh b/ehunter/io/VcfWriter.hh similarity index 88% rename from output/VcfWriter.hh rename to ehunter/io/VcfWriter.hh index 5f5ad49..b2a6e01 100644 --- a/output/VcfWriter.hh +++ b/ehunter/io/VcfWriter.hh @@ -27,10 +27,10 @@ #include #include -#include "common/Parameters.hh" -#include "common/Reference.hh" -#include "region_analysis/LocusFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Parameters.hh" +#include "core/Reference.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { @@ -74,8 +74,8 @@ public: private: void writeHeader(std::ostream& out); void writeBody(std::ostream& out); - using LocusIdAndVariantId = std::pair; - const std::vector getSortedIdPairs(); + using LocusIndexAndVariantId = std::pair; + const std::vector getSortedIdPairs(); std::string sampleId_; Reference& reference_; diff --git a/output/VcfWriterHelpers.cpp b/ehunter/io/VcfWriterHelpers.cpp similarity index 99% rename from output/VcfWriterHelpers.cpp rename to ehunter/io/VcfWriterHelpers.cpp index 2bad020..8a41651 100644 --- a/output/VcfWriterHelpers.cpp +++ b/ehunter/io/VcfWriterHelpers.cpp @@ -19,7 +19,7 @@ // // -#include "output/VcfWriterHelpers.hh" +#include "io/VcfWriterHelpers.hh" #include diff --git a/output/VcfWriterHelpers.hh b/ehunter/io/VcfWriterHelpers.hh similarity index 93% rename from output/VcfWriterHelpers.hh rename to ehunter/io/VcfWriterHelpers.hh index 364346a..00c2a1f 100644 --- a/output/VcfWriterHelpers.hh +++ b/ehunter/io/VcfWriterHelpers.hh @@ -26,9 +26,9 @@ #include #include -#include "common/Common.hh" -#include "region_analysis/VariantFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Common.hh" +#include "locus/LocusSpecification.hh" +#include "locus/VariantFindings.hh" namespace ehunter { diff --git a/ehunter/locus/CMakeLists.txt b/ehunter/locus/CMakeLists.txt new file mode 100644 index 0000000..59b80cb --- /dev/null +++ b/ehunter/locus/CMakeLists.txt @@ -0,0 +1,21 @@ +target_sources(ExpansionHunterLib # Requires CMake 3.13 or later + PRIVATE + IrrPairFinder.hh IrrPairFinder.cpp + LocusAligner.hh LocusAligner.cpp + LocusAnalyzer.hh LocusAnalyzer.cpp + LocusAnalyzerUtil.hh LocusAnalyzerUtil.cpp + LocusFindings.hh LocusFindings.cpp + LocusSpecification.hh LocusSpecification.cpp + RepeatAnalyzer.hh RepeatAnalyzer.cpp + SmallVariantAnalyzer.hh SmallVariantAnalyzer.cpp + VariantAnalyzer.hh VariantAnalyzer.cpp + VariantFindings.hh VariantFindings.cpp + VariantSpecification.hh VariantSpecification.cpp + ) + +target_sources(UnitTests # Requires CMake 3.13 or later + PRIVATE + IrrPairFinderTest.cpp + LocusAlignerTest.cpp + LocusAnalyzerTest.cpp + ) \ No newline at end of file diff --git a/region_analysis/Definitions.hh b/ehunter/locus/Definitions.hh similarity index 100% rename from region_analysis/Definitions.hh rename to ehunter/locus/Definitions.hh diff --git a/ehunter/locus/IrrPairFinder.cpp b/ehunter/locus/IrrPairFinder.cpp new file mode 100644 index 0000000..02209dc --- /dev/null +++ b/ehunter/locus/IrrPairFinder.cpp @@ -0,0 +1,47 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/IrrPairFinder.hh" + +#include + +using std::string; + +namespace ehunter +{ +namespace locus +{ + +IrrPairFinder::IrrPairFinder(string motif) + : targetMotif_(std::move(motif)) + , purityCalculator_(targetMotif_) +{ +} + +bool IrrPairFinder::check(const string& read, const string& mate) const +{ + const bool isFirstReadIrr = purityCalculator_.score(read) >= purityCutoff_; + const bool isSecondReadIrr = purityCalculator_.score(mate) >= purityCutoff_; + return isFirstReadIrr && isSecondReadIrr; +} + +} +} \ No newline at end of file diff --git a/ehunter/locus/IrrPairFinder.hh b/ehunter/locus/IrrPairFinder.hh new file mode 100644 index 0000000..b1e35bc --- /dev/null +++ b/ehunter/locus/IrrPairFinder.hh @@ -0,0 +1,46 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include + +#include "core/WeightedPurityCalculator.hh" + +namespace ehunter +{ +namespace locus +{ + +class IrrPairFinder +{ +public: + explicit IrrPairFinder(std::string motif); + + const std::string& targetMotif() const { return targetMotif_; } + bool check(const std::string& read, const std::string& mate) const; + +private: + std::string targetMotif_; + WeightedPurityCalculator purityCalculator_; + double purityCutoff_ = 0.90; +}; + +} +} diff --git a/ehunter/locus/IrrPairFinderTest.cpp b/ehunter/locus/IrrPairFinderTest.cpp new file mode 100644 index 0000000..0984977 --- /dev/null +++ b/ehunter/locus/IrrPairFinderTest.cpp @@ -0,0 +1,53 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/IrrPairFinder.hh" + +#include "gmock/gmock.h" + +using namespace ehunter; +using namespace locus; + +TEST(CreatingIrrPairFinder, TypicalArguments_Created) +{ + IrrPairFinder finder("CGG"); + ASSERT_EQ("CGG", finder.targetMotif()); +} + +TEST(CheckingForIrrPairs, PairOfPerfectIrrs_CheckPassed) +{ + IrrPairFinder finder("CGG"); + ASSERT_TRUE(finder.check("CGGCGGCG", "GGCGGC")); +} + +TEST(CheckingForIrrPairs, PairOfIrrsWithWrongMotif_CheckFailed) +{ + IrrPairFinder finder("ATA"); + ASSERT_FALSE(finder.check("CGGCGGCG", "GGCGGC")); +} + +TEST(CheckingForIrrPairs, NotIrrPair_CheckFailed) +{ + IrrPairFinder finder("CGG"); + ASSERT_FALSE(finder.check("ATACT", "GGCGGC")); + ASSERT_FALSE(finder.check("GGCGGC", "ATACT")); + ASSERT_FALSE(finder.check("ATACT", "ATACT")); +} diff --git a/ehunter/locus/LocusAligner.cpp b/ehunter/locus/LocusAligner.cpp new file mode 100644 index 0000000..cb9995e --- /dev/null +++ b/ehunter/locus/LocusAligner.cpp @@ -0,0 +1,92 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/LocusAligner.hh" + +#include "alignment/AlignmentFilters.hh" +#include "alignment/OperationsOnAlignments.hh" + +namespace ehunter +{ +namespace locus +{ + +LocusAligner::LocusAligner( + std::string locusId, GraphPtr graph, const HeuristicParameters& params, AlignWriterPtr writer) + : locusId_(std::move(locusId)) + , aligner_(graph, params.kmerLenForAlignment(), params.paddingLength(), params.seedAffixTrimLength()) + , orientationPredictor_(graph, params.orientationPredictorKmerLen(), params.orientationPredictorMinKmerCount()) + , writer_(std::move(writer)) +{ +} + +LocusAligner::AlignedPair LocusAligner::align(Read& read, Read* mate, graphtools::AlignerSelector& alignerSelector) +{ + auto readAlign = align(read, alignerSelector); + auto mateAlign = mate ? align(*mate, alignerSelector) : boost::none; + + int numMatchingBases = static_cast(static_cast(read.sequence().length()) / 7.5); + numMatchingBases = std::max(numMatchingBases, 10); + LinearAlignmentParameters parameters; + const int kMinNonRepeatAlignmentScore = numMatchingBases * parameters.matchScore; + + if (!checkIfLocallyPlacedReadPair(readAlign, mateAlign, kMinNonRepeatAlignmentScore)) + { + return { boost::none, boost::none }; + } + + if (readAlign && mateAlign) + { + writer_->write( + locusId_, read.fragmentId(), read.sequence(), read.isFirstMate(), read.isReversed(), read.isReversed(), + *readAlign); + writer_->write( + locusId_, mate->fragmentId(), mate->sequence(), mate->isFirstMate(), mate->isReversed(), mate->isReversed(), + *mateAlign); + } + + return { readAlign, mateAlign }; +} + +LocusAligner::OptionalAlign LocusAligner::align(Read& read, graphtools::AlignerSelector& alignerSelector) const +{ + OrientationPrediction predictedOrientation = orientationPredictor_.predict(read.sequence()); + + if (predictedOrientation == OrientationPrediction::kAlignsInReverseComplementOrientation) + { + read.reverseComplement(); + } + else if (predictedOrientation == OrientationPrediction::kDoesNotAlign) + { + return {}; + } + + auto readAligns = aligner_.align(read.sequence(), alignerSelector); + if (readAligns.empty()) + { + return {}; + } + + return computeCanonicalAlignment(readAligns); +} + +} +} diff --git a/ehunter/locus/LocusAligner.hh b/ehunter/locus/LocusAligner.hh new file mode 100644 index 0000000..295b8df --- /dev/null +++ b/ehunter/locus/LocusAligner.hh @@ -0,0 +1,65 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include + +#include + +#include "alignment/OrientationPredictor.hh" +#include "core/Parameters.hh" +#include "core/Read.hh" + +#include "graphalign/GappedAligner.hh" +#include "graphio/AlignmentWriter.hh" + +namespace ehunter +{ +namespace locus +{ + +class LocusAligner +{ +public: + using GraphPtr = const graphtools::Graph*; + using Align = graphtools::GraphAlignment; + using OptionalAlign = boost::optional; + using AlignedPair = std::pair; + using AlignWriterPtr = std::shared_ptr; + + LocusAligner(std::string locusId, GraphPtr graph, const HeuristicParameters& params, AlignWriterPtr writer); + + /// \param[in,out] alignerSelector A per-thread alignment workspace which mutates during alignment + /// + AlignedPair align(Read& read, Read* mate, graphtools::AlignerSelector& alignerSelector); + +private: + OptionalAlign align(Read& read, graphtools::AlignerSelector& alignerSelector) const; + + std::string locusId_; + graphtools::GappedGraphAligner aligner_; + OrientationPredictor orientationPredictor_; + AlignWriterPtr writer_; +}; + +} +} \ No newline at end of file diff --git a/ehunter/locus/LocusAlignerTest.cpp b/ehunter/locus/LocusAlignerTest.cpp new file mode 100644 index 0000000..f7982dc --- /dev/null +++ b/ehunter/locus/LocusAlignerTest.cpp @@ -0,0 +1,97 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/LocusAligner.hh" + +#include "gmock/gmock.h" + +#include "graphalign/GraphAlignmentOperations.hh" +#include "graphio/AlignmentWriter.hh" + +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" + +using namespace ehunter; +using namespace locus; + +using boost::optional; +using graphtools::BlankAlignmentWriter; +using graphtools::decodeGraphAlignment; +using graphtools::GraphAlignment; + +LocusAligner makeStrAligner(graphtools::Graph* graph) +{ + HeuristicParameters params(1000, 20, true, graphtools::AlignerType::DAG_ALIGNER, 4, 0, 0, 4, 1); + auto writer = std::make_shared(); + return { "str", graph, params, writer }; +} + +TEST(AligningReads, ReadPairFromSameStrand_Aligned) +{ + Read read(ReadId("frag1", MateNumber::kFirstMate), "ATTACC", true); + Read mate(ReadId("frag1", MateNumber::kSecondMate), "GGCGGC", true); + + auto graph = makeRegionGraph(decodeFeaturesFromRegex("ATATTA(C)*GGCGGC")); + auto aligner = makeStrAligner(&graph); + graphtools::AlignerSelector selector(graphtools::AlignerType::DAG_ALIGNER); + auto alignedPair = aligner.align(read, &mate, selector); + + GraphAlignment expectedReadAlign = decodeGraphAlignment(2, "0[4M]1[1M]1[1M]", &graph); + GraphAlignment expectedMateAlign = decodeGraphAlignment(0, "2[6M]", &graph); + + ASSERT_TRUE(alignedPair.first && alignedPair.second); // Both reads must be aligned + ASSERT_EQ(expectedReadAlign, *alignedPair.first); + ASSERT_EQ(expectedMateAlign, *alignedPair.second); +} + +TEST(AligningReads, ReadPairFromOppositeStrand_Aligned) +{ + Read read(ReadId("frag1", MateNumber::kFirstMate), "GGTAAT", true); + Read mate(ReadId("frag1", MateNumber::kSecondMate), "GCCGCC", false); + + auto graph = makeRegionGraph(decodeFeaturesFromRegex("ATATTA(C)*GGCGGC")); + auto aligner = makeStrAligner(&graph); + graphtools::AlignerSelector selector(graphtools::AlignerType::DAG_ALIGNER); + auto alignedPair = aligner.align(read, &mate, selector); + + GraphAlignment expectedReadAlign = decodeGraphAlignment(2, "0[4M]1[1M]1[1M]", &graph); + GraphAlignment expectedMateAlign = decodeGraphAlignment(0, "2[6M]", &graph); + + ASSERT_TRUE(alignedPair.first && alignedPair.second); // Both reads must be aligned + ASSERT_EQ(expectedReadAlign, *alignedPair.first); + ASSERT_EQ(expectedMateAlign, *alignedPair.second); + ASSERT_FALSE(read.isReversed()); + ASSERT_TRUE(mate.isReversed()); +} + +TEST(AligningReads, NotLocallyPlasedReads_NotAligned) +{ + Read read(ReadId("frag1", MateNumber::kFirstMate), "TACCC", true); + Read mate(ReadId("frag1", MateNumber::kSecondMate), "CCCGG", false); + + auto graph = makeRegionGraph(decodeFeaturesFromRegex("ATATTA(C)*GGCGGC")); + auto aligner = makeStrAligner(&graph); + graphtools::AlignerSelector selector(graphtools::AlignerType::DAG_ALIGNER); + auto alignedPair = aligner.align(read, &mate, selector); + + ASSERT_FALSE(alignedPair.first); + ASSERT_FALSE(alignedPair.second); +} diff --git a/ehunter/locus/LocusAnalyzer.cpp b/ehunter/locus/LocusAnalyzer.cpp new file mode 100644 index 0000000..4549cd6 --- /dev/null +++ b/ehunter/locus/LocusAnalyzer.cpp @@ -0,0 +1,199 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/LocusAnalyzer.hh" + +#include + +#include "locus/LocusAligner.hh" +#include "locus/RepeatAnalyzer.hh" +#include "locus/SmallVariantAnalyzer.hh" + +using boost::make_unique; +using boost::optional; +using graphtools::AlignmentWriter; +using graphtools::GraphAlignment; +using graphtools::NodeId; +using std::string; +using std::vector; + +namespace ehunter +{ +namespace locus +{ + +LocusAnalyzer::LocusAnalyzer(LocusSpecification locusSpec, const HeuristicParameters& params, AlignWriterPtr writer) + : locusSpec_(std::move(locusSpec)) + , aligner_(locusSpec_.locusId(), &locusSpec_.regionGraph(), params, std::move(writer)) + , statsCalc_(locusSpec_.typeOfChromLocusLocatedOn(), locusSpec_.regionGraph()) +{ + for (const auto& variantSpec : locusSpec_.variantSpecs()) + { + if (variantSpec.classification().type == VariantType::kRepeat) + { + const auto& graph = locusSpec_.regionGraph(); + const int repeatNodeId = static_cast(variantSpec.nodes().front()); + const auto& motif = graph.nodeSeq(repeatNodeId); + + if (variantSpec.classification().subtype == VariantSubtype::kRareRepeat) + { + if (irrPairFinder()) + { + const string message + = "Region " + locusSpec_.locusId() + " must not have more than one rare repeat"; + throw std::logic_error(message); + } + addIrrPairFinder(motif); + } + + addRepeatAnalyzer(variantSpec.id(), repeatNodeId); + } + else if (variantSpec.classification().type == VariantType::kSmallVariant) + { + addSmallVariantAnalyzer( + variantSpec.id(), variantSpec.classification().subtype, variantSpec.nodes(), + variantSpec.optionalRefNode()); + } + else + { + std::stringstream encoding; + encoding << variantSpec.classification().type << "/" << variantSpec.classification().subtype; + throw std::logic_error("Missing logic to create an analyzer for " + encoding.str()); + } + } +} + +void LocusAnalyzer::processMates( + Read& read, Read* mate, RegionType regionType, graphtools::AlignerSelector& alignerSelector) +{ + if (regionType == RegionType::kTarget) + { + processOntargetMates(read, mate, alignerSelector); + } + else if (mate) + { + processOfftargetMates(read, *mate); + } +} + +void LocusAnalyzer::processOntargetMates(Read& read, Read* mate, graphtools::AlignerSelector& alignerSelector) +{ + auto alignedPair = aligner_.align(read, mate, alignerSelector); + + const bool neitherMateAligned = !alignedPair.first && !alignedPair.second; + const bool bothMatesAligned = alignedPair.first && alignedPair.second; + + if (irrPairFinder_ && neitherMateAligned && mate) + { + processOfftargetMates(read, *mate); + return; + } + + if (bothMatesAligned) + { + statsCalc_.inspect(*alignedPair.first, *alignedPair.second); + runVariantAnalysis(read, *alignedPair.first, *mate, *alignedPair.second); + } + else + { + if (alignedPair.first) { + statsCalc_.inspectRead(*alignedPair.first); + } + if (alignedPair.second) { + statsCalc_.inspectRead(*alignedPair.second); + } + } +} + +void LocusAnalyzer::processOfftargetMates(const Read& read, const Read& mate) +{ + if (!irrPairFinder_) + { + const string message = "Locus " + locusSpec_.locusId() + " is not supposed to have offtarget read pairs"; + throw std::logic_error(message); + } + + if (irrPairFinder_->check(read.sequence(), mate.sequence())) + { + int numAnalyzersFound = 0; + for (auto& variantAnalyzer : variantAnalyzers_) + { + auto repeatAnalyzer = dynamic_cast(variantAnalyzer.get()); + if (repeatAnalyzer != nullptr && repeatAnalyzer->repeatUnit() == irrPairFinder_->targetMotif()) + { + numAnalyzersFound++; + repeatAnalyzer->addInrepeatReadPair(); + } + } + + if (numAnalyzersFound != 1) + { + const string message = "Locus " + locusSpec_.locusId() + " must have exactly one rare motif"; + throw std::logic_error(message); + } + } +} + +LocusFindings LocusAnalyzer::analyze(Sex sampleSex, boost::optional genomeWideDepth) +{ + LocusFindings locusFindings(statsCalc_.estimate(sampleSex)); + if (genomeWideDepth && locusSpec_.requiresGenomeWideDepth()) + { + locusFindings.stats.setDepth(*genomeWideDepth); + } + + for (auto& variantAnalyzer : variantAnalyzers_) + { + std::unique_ptr variantFindingsPtr = variantAnalyzer->analyze(locusFindings.stats); + const string& variantId = variantAnalyzer->variantId(); + locusFindings.findingsForEachVariant.emplace(variantId, std::move(variantFindingsPtr)); + } + + return locusFindings; +} + +void LocusAnalyzer::addIrrPairFinder(std::string motif) { irrPairFinder_ = IrrPairFinder(std::move(motif)); } + +void LocusAnalyzer::addRepeatAnalyzer(std::string variantId, graphtools::NodeId nodeId) +{ + variantAnalyzers_.emplace_back(make_unique( + std::move(variantId), locusSpec_.regionGraph(), nodeId, locusSpec_.genotyperParameters())); +} + +void LocusAnalyzer::addSmallVariantAnalyzer( + string variantId, VariantSubtype subtype, vector nodes, optional refNode) +{ + variantAnalyzers_.emplace_back(make_unique( + std::move(variantId), subtype, locusSpec_.regionGraph(), std::move(nodes), refNode, + locusSpec_.genotyperParameters())); +} + +void LocusAnalyzer::runVariantAnalysis( + const Read& read, const LocusAnalyzer::Align& readAlign, const Read& mate, const LocusAnalyzer::Align& mateAlign) +{ + for (auto& analyzer : variantAnalyzers_) + { + analyzer->processMates(read, readAlign, mate, mateAlign); + } +} + +} +} \ No newline at end of file diff --git a/ehunter/locus/LocusAnalyzer.hh b/ehunter/locus/LocusAnalyzer.hh new file mode 100644 index 0000000..13b7102 --- /dev/null +++ b/ehunter/locus/LocusAnalyzer.hh @@ -0,0 +1,93 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include +#include +#include + +#include + +#include "graphalign/GappedAligner.hh" +#include "graphio/AlignmentWriter.hh" + +#include "core/GenomicRegion.hh" +#include "core/LocusStats.hh" +#include "core/Read.hh" +#include "locus/IrrPairFinder.hh" +#include "locus/LocusAligner.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" +#include "locus/VariantAnalyzer.hh" + +namespace ehunter +{ +namespace locus +{ + +// Regions of the reference genome that can contain reads that originated in a given locus are partitioned into target +// and offtarget regions. Target regions typically consist of the reference region of the locus and possibly other +// highly-similar regions where reads typically misalign. Offtarget regions are regions where certain kinds of +// relevant reads might occasionally misalign and that require special handling (usually for efficiency reasons). +enum class RegionType +{ + kTarget, + kOfftarget +}; + +using AlignWriterPtr = std::shared_ptr; + +class LocusAnalyzer +{ +public: + using Align = graphtools::GraphAlignment; + using Node = graphtools::NodeId; + + LocusAnalyzer(LocusSpecification locusSpec, const HeuristicParameters& params, AlignWriterPtr writer); + + const std::string& locusId() const { return locusSpec_.locusId(); } + const LocusSpecification& locusSpec() const { return locusSpec_; } + + void processMates(Read& read, Read* mate, RegionType regionType, graphtools::AlignerSelector& alignerSelector); + LocusFindings analyze(Sex sampleSex, boost::optional genomeWideDepth); + + const boost::optional& irrPairFinder() const { return irrPairFinder_; } + void addIrrPairFinder(std::string motif); + + void addRepeatAnalyzer(std::string variantId, Node nodeId); + void addSmallVariantAnalyzer( + std::string variantId, VariantSubtype subtype, std::vector nodes, boost::optional refNode); + +private: + void processOntargetMates(Read& read, Read* mate, graphtools::AlignerSelector& alignerSelector); + void processOfftargetMates(const Read& read, const Read& mate); + void runVariantAnalysis(const Read& read, const Align& readAlign, const Read& mate, const Align& mateAlign); + + LocusSpecification locusSpec_; + LocusAligner aligner_; + LocusStatsCalculator statsCalc_; + boost::optional irrPairFinder_; + std::vector> variantAnalyzers_; +}; + +} +} diff --git a/ehunter/locus/LocusAnalyzerTest.cpp b/ehunter/locus/LocusAnalyzerTest.cpp new file mode 100644 index 0000000..891cf7c --- /dev/null +++ b/ehunter/locus/LocusAnalyzerTest.cpp @@ -0,0 +1,74 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/LocusAnalyzer.hh" + +#include "gtest/gtest.h" + +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" + +using namespace ehunter; +using namespace locus; + +using graphtools::AlignerType; +using std::vector; + +LocusSpecification buildStrSpec(const std::string& structure) +{ + auto graph = makeRegionGraph(decodeFeaturesFromRegex(structure)); + vector referenceRegions = { GenomicRegion(1, 1, 2) }; + + NodeToRegionAssociation dummyAssociation; + GenotyperParameters params; + LocusSpecification locusSpec("region", ChromType::kAutosome, referenceRegions, graph, dummyAssociation, params); + VariantClassification classification(VariantType::kRepeat, VariantSubtype::kCommonRepeat); + locusSpec.addVariantSpecification("repeat", classification, GenomicRegion(1, 1, 2), { 1 }, 1); + return locusSpec; +} + +TEST(CreatingLocusAnalyzer, TypicalParameters_Created) +{ + auto locusSpec = buildStrSpec("ATTCGA(C)*ATGTCG"); + + HeuristicParameters heuristicParams(1000, 20, true, AlignerType::DAG_ALIGNER, 4, 1, 5, 4, 1); + auto writer = std::make_shared(); + + graphtools::AlignerSelector selector(heuristicParams.alignerType()); + LocusAnalyzer locusAnalyzer(locusSpec, heuristicParams, writer); + + Read read1(ReadId("read1", MateNumber::kFirstMate), "CGACCCATGT", true); + Read mate1(ReadId("read1", MateNumber::kSecondMate), "GACCCATGTC", true); + locusAnalyzer.processMates(read1, &mate1, RegionType::kTarget, selector); + + Read read2(ReadId("read2", MateNumber::kFirstMate), "CGACATGT", true); + Read mate2(ReadId("read2", MateNumber::kSecondMate), "GACATGTC", true); + locusAnalyzer.processMates(read2, &mate2, RegionType::kTarget, selector); + + LocusFindings locusFindings = locusAnalyzer.analyze(Sex::kFemale, boost::none); + RepeatFindings observed = *dynamic_cast(locusFindings.findingsForEachVariant["repeat"].get()); + + CountTable spanningCounts({ { 1, 2 }, { 3, 2 } }); + RepeatGenotype genotype(1, { 1, 3 }); + RepeatFindings repeatFindings(spanningCounts, {}, {}, AlleleCount::kTwo, genotype, {}); + + ASSERT_EQ(repeatFindings, observed); +} diff --git a/ehunter/locus/LocusAnalyzerUtil.cpp b/ehunter/locus/LocusAnalyzerUtil.cpp new file mode 100644 index 0000000..a24469d --- /dev/null +++ b/ehunter/locus/LocusAnalyzerUtil.cpp @@ -0,0 +1,155 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "locus/LocusAnalyzerUtil.hh" + +#include +#include + +#include "spdlog/spdlog.h" + +// Note that ostr.h must be included after spdlog.h +#include "spdlog/fmt/ostr.h" + +namespace ehunter +{ +namespace locus +{ +namespace +{ + +struct LocusInitThreadSharedData +{ + LocusInitThreadSharedData() + : isWorkerThreadException(false) + , locusIndex(0) + { + } + + std::atomic isWorkerThreadException; + std::atomic locusIndex; +}; + +/// \brief Data isolated to each locus-initialization thread +/// +struct LocusThreadLocalData +{ + std::exception_ptr threadExceptionPtr = nullptr; +}; + +/// \brief Initialize a series of locus analyzers on one thread +/// +void initializeLocusAnalyzerThread( + const int threadIndex, const RegionCatalog& regionCatalog, const HeuristicParameters& heuristicParams, + AlignWriterPtr bamletWriter, std::vector>& locusAnalyzers, + LocusInitThreadSharedData& locusInitThreadSharedData, + std::vector& locusInitThreadLocalDataPool) +{ + LocusThreadLocalData& locusThreadData(locusInitThreadLocalDataPool[threadIndex]); + std::string locusId = "Unknown"; + + try + { + const unsigned size(regionCatalog.size()); + while (true) + { + if (locusInitThreadSharedData.isWorkerThreadException.load()) + { + return; + } + const auto locusIndex(locusInitThreadSharedData.locusIndex.fetch_add(1)); + if (locusIndex >= size) + { + return; + } + + const auto& locusSpec(regionCatalog[locusIndex]); + locusId = locusSpec.locusId(); + + locusAnalyzers[locusIndex].reset(new LocusAnalyzer(locusSpec, heuristicParams, bamletWriter)); + } + } + catch (const std::exception& e) + { + locusInitThreadSharedData.isWorkerThreadException.store(true); + locusThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error( + "Exception caught in thread {} while initializing locus: {} : {}", threadIndex, locusId, e.what()); + throw; + } + catch (...) + { + locusInitThreadSharedData.isWorkerThreadException.store(true); + locusThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error("Exception caught in thread {} while initializing locus: {}", threadIndex, locusId); + throw; + } +} + +} + +std::vector> initializeLocusAnalyzers( + const RegionCatalog& regionCatalog, const HeuristicParameters& heuristicParams, AlignWriterPtr bamletWriter, + const int threadCount) +{ + assert(threadCount >= 1); + + std::vector> locusAnalyzers; + locusAnalyzers.resize(regionCatalog.size()); + + LocusInitThreadSharedData locusInitThreadSharedData; + std::vector locusInitThreadLocalDataPool(threadCount); + + std::vector initThreads; + for (int threadIndex(0); threadIndex < threadCount; threadIndex++) + { + initThreads.emplace_back( + initializeLocusAnalyzerThread, threadIndex, std::cref(regionCatalog), std::cref(heuristicParams), + bamletWriter, std::ref(locusAnalyzers), std::ref(locusInitThreadSharedData), + std::ref(locusInitThreadLocalDataPool)); + } + + // Rethrow exceptions from worker pool in thread order: + if (locusInitThreadSharedData.isWorkerThreadException.load()) + { + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + const auto& locusThreadData(locusInitThreadLocalDataPool[threadIndex]); + if (locusThreadData.threadExceptionPtr) + { + std::rethrow_exception(locusThreadData.threadExceptionPtr); + } + } + } + + for (auto& initThread : initThreads) + { + initThread.join(); + } + + return locusAnalyzers; +} + +} +} diff --git a/ehunter/locus/LocusAnalyzerUtil.hh b/ehunter/locus/LocusAnalyzerUtil.hh new file mode 100644 index 0000000..dafe108 --- /dev/null +++ b/ehunter/locus/LocusAnalyzerUtil.hh @@ -0,0 +1,41 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include "locus/LocusAnalyzer.hh" + +namespace ehunter +{ +namespace locus +{ + +/// Initialize a LocusAnalyzer for each locus in \p regionCatalog +/// +/// \param[in] threadCount Number of threads to distribute initialization over +/// +std::vector> initializeLocusAnalyzers( + const RegionCatalog& regionCatalog, const HeuristicParameters& heuristicParams, AlignWriterPtr alignmentWriter, + int threadCount); + +} +} diff --git a/region_analysis/LocusFindings.cpp b/ehunter/locus/LocusFindings.cpp similarity index 94% rename from region_analysis/LocusFindings.cpp rename to ehunter/locus/LocusFindings.cpp index a130747..61f662f 100644 --- a/region_analysis/LocusFindings.cpp +++ b/ehunter/locus/LocusFindings.cpp @@ -19,4 +19,4 @@ // // -#include "region_analysis/LocusFindings.hh" +#include "locus/LocusFindings.hh" diff --git a/region_analysis/LocusFindings.hh b/ehunter/locus/LocusFindings.hh similarity index 85% rename from region_analysis/LocusFindings.hh rename to ehunter/locus/LocusFindings.hh index c169611..8b285c3 100644 --- a/region_analysis/LocusFindings.hh +++ b/ehunter/locus/LocusFindings.hh @@ -25,8 +25,8 @@ #include #include -#include "region_analysis/VariantFindings.hh" -#include "stats/LocusStats.hh" +#include "core/LocusStats.hh" +#include "locus/VariantFindings.hh" namespace ehunter { @@ -34,7 +34,7 @@ namespace ehunter // Container with per-locus analysis results struct LocusFindings { - explicit LocusFindings(LocusStats stats) + explicit LocusFindings(LocusStats stats = {}) : stats(stats) { } @@ -43,6 +43,6 @@ struct LocusFindings std::unordered_map> findingsForEachVariant; }; -using SampleFindings = std::unordered_map; +using SampleFindings = std::vector; } diff --git a/region_spec/LocusSpecification.cpp b/ehunter/locus/LocusSpecification.cpp similarity index 94% rename from region_spec/LocusSpecification.cpp rename to ehunter/locus/LocusSpecification.cpp index 2cae4f5..d14cc34 100644 --- a/region_spec/LocusSpecification.cpp +++ b/ehunter/locus/LocusSpecification.cpp @@ -21,7 +21,7 @@ // // -#include "region_spec/LocusSpecification.hh" +#include "locus/LocusSpecification.hh" #include #include @@ -32,11 +32,11 @@ #include #include +#include "spdlog/spdlog.h" #include "thirdparty/json/json.hpp" -#include "thirdparty/spdlog/include/spdlog/spdlog.h" -#include "common/Common.hh" -#include "common/Reference.hh" +#include "core/Common.hh" +#include "core/Reference.hh" using boost::optional; using graphtools::NodeId; diff --git a/region_spec/LocusSpecification.hh b/ehunter/locus/LocusSpecification.hh similarity index 94% rename from region_spec/LocusSpecification.hh rename to ehunter/locus/LocusSpecification.hh index 3d36b68..c3029ca 100644 --- a/region_spec/LocusSpecification.hh +++ b/ehunter/locus/LocusSpecification.hh @@ -34,10 +34,10 @@ #include "graphcore/Graph.hh" #include "thirdparty/json/json.hpp" -#include "common/Common.hh" -#include "common/GenomicRegion.hh" -#include "common/Reference.hh" -#include "region_spec/VariantSpecification.hh" +#include "core/Common.hh" +#include "core/GenomicRegion.hh" +#include "core/Reference.hh" +#include "locus/VariantSpecification.hh" namespace ehunter { @@ -94,6 +94,6 @@ private: GenotyperParameters parameters_; }; -using RegionCatalog = std::map; +using RegionCatalog = std::vector; } diff --git a/region_analysis/RepeatAnalyzer.cpp b/ehunter/locus/RepeatAnalyzer.cpp similarity index 96% rename from region_analysis/RepeatAnalyzer.cpp rename to ehunter/locus/RepeatAnalyzer.cpp index 05ac608..4a23de0 100644 --- a/region_analysis/RepeatAnalyzer.cpp +++ b/ehunter/locus/RepeatAnalyzer.cpp @@ -19,12 +19,12 @@ // // -#include "region_analysis/RepeatAnalyzer.hh" +#include "locus/RepeatAnalyzer.hh" // clang-format off // Note that spdlog.h must be included before ostr.h -#include "thirdparty/spdlog/include/spdlog/spdlog.h" -#include "thirdparty/spdlog/include/spdlog/fmt/ostr.h" +#include "spdlog/spdlog.h" +#include "spdlog/fmt/ostr.h" // clang-format on #include "graphalign/GraphAlignmentOperations.hh" @@ -32,8 +32,8 @@ #include "alignment/AlignmentFilters.hh" #include "alignment/OperationsOnAlignments.hh" -#include "genotyping/StrGenotyper.hh" #include "genotyping/AlignMatrixFiltering.hh" +#include "genotyping/StrGenotyper.hh" namespace ehunter { diff --git a/region_analysis/RepeatAnalyzer.hh b/ehunter/locus/RepeatAnalyzer.hh similarity index 92% rename from region_analysis/RepeatAnalyzer.hh rename to ehunter/locus/RepeatAnalyzer.hh index 6274b2a..e2bbbd6 100644 --- a/region_analysis/RepeatAnalyzer.hh +++ b/ehunter/locus/RepeatAnalyzer.hh @@ -30,12 +30,12 @@ #include "graphalign/GraphAlignment.hh" #include "graphcore/Graph.hh" -#include "classification/AlignmentClassifier.hh" -#include "filtering/GraphVariantAlignmentStats.hh" +#include "alignment/AlignmentClassifier.hh" +#include "alignment/GraphVariantAlignmentStats.hh" +#include "core/Read.hh" #include "genotyping/AlignMatrix.hh" #include "genotyping/RepeatGenotype.hh" -#include "reads/Read.hh" -#include "region_analysis/VariantAnalyzer.hh" +#include "locus/VariantAnalyzer.hh" namespace ehunter { diff --git a/region_analysis/SmallVariantAnalyzer.cpp b/ehunter/locus/SmallVariantAnalyzer.cpp similarity index 99% rename from region_analysis/SmallVariantAnalyzer.cpp rename to ehunter/locus/SmallVariantAnalyzer.cpp index 6a04c04..b034c56 100644 --- a/region_analysis/SmallVariantAnalyzer.cpp +++ b/ehunter/locus/SmallVariantAnalyzer.cpp @@ -19,7 +19,7 @@ // // -#include "region_analysis/SmallVariantAnalyzer.hh" +#include "locus/SmallVariantAnalyzer.hh" #include diff --git a/region_analysis/SmallVariantAnalyzer.hh b/ehunter/locus/SmallVariantAnalyzer.hh similarity index 89% rename from region_analysis/SmallVariantAnalyzer.hh rename to ehunter/locus/SmallVariantAnalyzer.hh index 2c762a1..1cdf325 100644 --- a/region_analysis/SmallVariantAnalyzer.hh +++ b/ehunter/locus/SmallVariantAnalyzer.hh @@ -23,14 +23,14 @@ #include -#include "thirdparty/spdlog/include/spdlog/spdlog.h" +#include "spdlog/spdlog.h" -#include "classification/ClassifierOfAlignmentsToVariant.hh" -#include "filtering/GraphVariantAlignmentStats.hh" +#include "alignment/ClassifierOfAlignmentsToVariant.hh" +#include "alignment/GraphVariantAlignmentStats.hh" #include "genotyping/AlleleChecker.hh" #include "genotyping/SmallVariantGenotyper.hh" -#include "region_analysis/VariantAnalyzer.hh" -#include "region_spec/VariantSpecification.hh" +#include "locus/VariantAnalyzer.hh" +#include "locus/VariantSpecification.hh" namespace ehunter { diff --git a/region_analysis/VariantAnalyzer.cpp b/ehunter/locus/VariantAnalyzer.cpp similarity index 93% rename from region_analysis/VariantAnalyzer.cpp rename to ehunter/locus/VariantAnalyzer.cpp index 206b0f8..292fa34 100644 --- a/region_analysis/VariantAnalyzer.cpp +++ b/ehunter/locus/VariantAnalyzer.cpp @@ -19,4 +19,4 @@ // // -#include "region_analysis/VariantAnalyzer.hh" +#include "locus/VariantAnalyzer.hh" diff --git a/region_analysis/VariantAnalyzer.hh b/ehunter/locus/VariantAnalyzer.hh similarity index 92% rename from region_analysis/VariantAnalyzer.hh rename to ehunter/locus/VariantAnalyzer.hh index 426ce4c..0765aa1 100644 --- a/region_analysis/VariantAnalyzer.hh +++ b/ehunter/locus/VariantAnalyzer.hh @@ -28,11 +28,11 @@ #include "graphalign/GraphAlignment.hh" #include "graphcore/Graph.hh" -#include "common/Common.hh" -#include "common/Parameters.hh" -#include "reads/Read.hh" -#include "region_analysis/VariantFindings.hh" -#include "stats/LocusStats.hh" +#include "core/Common.hh" +#include "core/LocusStats.hh" +#include "core/Parameters.hh" +#include "core/Read.hh" +#include "locus/VariantFindings.hh" namespace ehunter { diff --git a/region_analysis/VariantFindings.cpp b/ehunter/locus/VariantFindings.cpp similarity index 97% rename from region_analysis/VariantFindings.cpp rename to ehunter/locus/VariantFindings.cpp index f1fc729..a17ca0c 100644 --- a/region_analysis/VariantFindings.cpp +++ b/ehunter/locus/VariantFindings.cpp @@ -19,7 +19,7 @@ // // -#include "region_analysis/VariantFindings.hh" +#include "locus/VariantFindings.hh" using std::string; diff --git a/region_analysis/VariantFindings.hh b/ehunter/locus/VariantFindings.hh similarity index 99% rename from region_analysis/VariantFindings.hh rename to ehunter/locus/VariantFindings.hh index 6127c9c..cec3d39 100644 --- a/region_analysis/VariantFindings.hh +++ b/ehunter/locus/VariantFindings.hh @@ -26,7 +26,7 @@ #include -#include "common/CountTable.hh" +#include "core/CountTable.hh" #include "genotyping/AlleleChecker.hh" #include "genotyping/RepeatGenotype.hh" #include "genotyping/SmallVariantGenotype.hh" diff --git a/region_spec/VariantSpecification.cpp b/ehunter/locus/VariantSpecification.cpp similarity index 98% rename from region_spec/VariantSpecification.cpp rename to ehunter/locus/VariantSpecification.cpp index 163a976..48388b1 100644 --- a/region_spec/VariantSpecification.cpp +++ b/ehunter/locus/VariantSpecification.cpp @@ -21,7 +21,7 @@ // // -#include "region_spec/VariantSpecification.hh" +#include "locus/VariantSpecification.hh" #include diff --git a/region_spec/VariantSpecification.hh b/ehunter/locus/VariantSpecification.hh similarity index 97% rename from region_spec/VariantSpecification.hh rename to ehunter/locus/VariantSpecification.hh index 7c6c6db..4ccddc4 100644 --- a/region_spec/VariantSpecification.hh +++ b/ehunter/locus/VariantSpecification.hh @@ -32,9 +32,9 @@ #include "graphcore/Graph.hh" -#include "common/Common.hh" -#include "common/Parameters.hh" -#include "common/GenomicRegion.hh" +#include "core/Common.hh" +#include "core/GenomicRegion.hh" +#include "core/Parameters.hh" namespace ehunter { diff --git a/ehunter/sample/AnalyzerFinder.cpp b/ehunter/sample/AnalyzerFinder.cpp new file mode 100644 index 0000000..648daf6 --- /dev/null +++ b/ehunter/sample/AnalyzerFinder.cpp @@ -0,0 +1,259 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko , +// Felix Schlesinger +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/AnalyzerFinder.hh" + +using boost::optional; +using ehunter::locus::LocusAnalyzer; +using ehunter::locus::RegionType; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace ehunter +{ + +namespace +{ +bool areMatesNearby(int32_t readContigId, int64_t readPosition, int32_t mateContigId, int64_t matePosition) +{ + const int kMaxMateDistance = 1000; + return ((readContigId == mateContigId) && (std::abs(readPosition - matePosition) < kMaxMateDistance)); +} + +inline RegionType coalesceRegionTypes(RegionType readRegionType, RegionType mateRegionType) +{ + if (readRegionType == RegionType::kTarget || mateRegionType == RegionType::kTarget) + { + return RegionType::kTarget; + } + + return RegionType::kOfftarget; +} + +/// Remove items from \p bundles which refer to a LocusAnalyzer already found in \p commonBundles +/// +void filterOutCommonBundles(const vector& commonBundles, vector& bundles) +{ + if (commonBundles.size() == bundles.size()) + { + bundles.clear(); + } + else + { + vector remainingBundles; + for (auto& bundle : bundles) + { + bool bundleAssigned(false); + for (const auto& commonBundle : commonBundles) + { + if (bundle.locusIndex == commonBundle.locusIndex) + { + bundleAssigned = true; + break; + } + } + if (not bundleAssigned) + { + remainingBundles.emplace_back(bundle); + } + } + bundles = remainingBundles; + } +} + +/// \param[in,out] readBundles Used to find common bundles, updated to contain any remaining bundles on return +/// +/// \param[in,out] mateBundles Used to find common bundles, updated to contain any remaining bundles on return +/// +vector coalesceCommonBundles(vector& readBundles, vector& mateBundles) +{ + vector commonBundles; + + for (const auto& readBundle : readBundles) + { + for (const auto& mateBundle : mateBundles) + { + if (readBundle.locusIndex == mateBundle.locusIndex) + { + commonBundles.emplace_back(readBundle); + commonBundles.back().regionType = coalesceRegionTypes(readBundle.regionType, mateBundle.regionType); + break; + } + } + } + + if (not commonBundles.empty()) + { + filterOutCommonBundles(commonBundles, readBundles); + filterOutCommonBundles(commonBundles, mateBundles); + } + + return commonBundles; +} + +/// We ignore nearby pairs where one mate is inside and one mate is outside of the offtarget region +/// +/// \param[in,out] bundles Coalesced bundles are appended to this structure +/// +void coalesceBundlesForNearbyMates( + const vector& readBundles, const vector& mateBundles, + vector& bundles) +{ + for (const auto& bundle : readBundles) + { + if (bundle.regionType == RegionType::kTarget) + { + bundles.push_back(bundle); + bundles.back().inputType = AnalyzerInputType::kReadOnly; + } + } + + for (const auto& bundle : mateBundles) + { + if (bundle.regionType == RegionType::kTarget) + { + bundles.push_back(bundle); + bundles.back().inputType = AnalyzerInputType::kMateOnly; + } + } +} + +/// \param[in,out] bundles Coalesced bundles are appended to this structure +/// +void coalesceBundlesForFarawayMates( + const vector& readBundles, const vector& mateBundles, + vector& bundles) +{ + for (const auto& bundle : readBundles) + { + bundles.push_back(bundle); + bundles.back().inputType = AnalyzerInputType::kBothReads; + } + + for (const auto& bundle : mateBundles) + { + bundles.push_back(bundle); + bundles.back().inputType = AnalyzerInputType::kBothReads; + } +} +} + +void processAnalyzerBundleReadPair( + locus::LocusAnalyzer& locusAnalyzer, locus::RegionType regionType, AnalyzerInputType inputType, Read& read, + Read& mate, graphtools::AlignerSelector& alignerSelector) +{ + + switch (inputType) + { + case AnalyzerInputType::kBothReads: + locusAnalyzer.processMates(read, &mate, regionType, alignerSelector); + break; + case AnalyzerInputType::kReadOnly: + locusAnalyzer.processMates(read, nullptr, regionType, alignerSelector); + break; + case AnalyzerInputType::kMateOnly: + locusAnalyzer.processMates(mate, nullptr, regionType, alignerSelector); + break; + } +} + +AnalyzerFinder::AnalyzerFinder(vector>& locusAnalyzers) +{ + using IntervalWithLocusTypeAndAnalyzer = Interval; + + unordered_map> contigToIntervals; + + const unsigned locusAnalzerCount(locusAnalyzers.size()); + for (unsigned locusAnalyzerIndex(0); locusAnalyzerIndex < locusAnalzerCount; ++locusAnalyzerIndex) + { + const auto& locusAnalyzer(locusAnalyzers[locusAnalyzerIndex]); + const LocusSpecification& locusSpec = locusAnalyzer->locusSpec(); + for (const auto& region : locusSpec.targetReadExtractionRegions()) + { + AnalyzerBundle bundle(RegionType::kTarget, locusAnalyzerIndex); + contigToIntervals[region.contigIndex()].emplace_back(region.start(), region.end(), bundle); + } + + for (const auto& region : locusSpec.offtargetReadExtractionRegions()) + { + AnalyzerBundle bundle(RegionType::kOfftarget, locusAnalyzerIndex); + contigToIntervals[region.contigIndex()].emplace_back(region.start(), region.end(), bundle); + } + } + + for (auto& contigAndIntervals : contigToIntervals) + { + int32_t contigIndex = contigAndIntervals.first; + auto intervals = contigAndIntervals.second; + + intervalTrees_.emplace(std::make_pair(contigIndex, AnalyzerIntervalTree(std::move(intervals)))); + } +} + +vector AnalyzerFinder::query(int32_t contigIndex, int64_t start, int64_t end) const +{ + const auto contigTreeIterator = intervalTrees_.find(contigIndex); + if (contigTreeIterator == intervalTrees_.end()) + { + return vector(); + } + + const auto& intervalsWithBundles = contigTreeIterator->second.findOverlapping(start, end); + + vector analyzerBundles; + for (const auto& intervalWithBundle : intervalsWithBundles) + { + const bool isReadContainedInInterval = static_cast(intervalWithBundle.start) <= start + && end <= static_cast(intervalWithBundle.stop); + if (isReadContainedInInterval) + { + analyzerBundles.push_back(intervalWithBundle.value); + } + } + + return analyzerBundles; +} + +vector AnalyzerFinder::query( + int32_t readContigId, int64_t readStart, int64_t readEnd, int32_t mateContigId, int64_t mateStart, + int64_t mateEnd) const +{ + vector readAnalyzerBundles = query(readContigId, readStart, readEnd); + vector mateAnalyzerBundles = query(mateContigId, mateStart, mateEnd); + vector bundles = coalesceCommonBundles(readAnalyzerBundles, mateAnalyzerBundles); + + if ((not readAnalyzerBundles.empty()) or (not mateAnalyzerBundles.empty())) + { + if (areMatesNearby(readContigId, readStart, mateContigId, mateStart)) + { + coalesceBundlesForNearbyMates(readAnalyzerBundles, mateAnalyzerBundles, bundles); + } + else + { + coalesceBundlesForFarawayMates(readAnalyzerBundles, mateAnalyzerBundles, bundles); + } + } + return bundles; +} + +} diff --git a/sample_analysis/AnalyzerFinder.hh b/ehunter/sample/AnalyzerFinder.hh similarity index 75% rename from sample_analysis/AnalyzerFinder.hh rename to ehunter/sample/AnalyzerFinder.hh index 0028578..6130eda 100644 --- a/sample_analysis/AnalyzerFinder.hh +++ b/ehunter/sample/AnalyzerFinder.hh @@ -28,9 +28,9 @@ #include "thirdparty/intervaltree/IntervalTree.h" -#include "reads/Read.hh" -#include "region_analysis/LocusAnalyzer.hh" -#include "sample_analysis/GenomeMask.hh" +#include "core/Read.hh" +#include "locus/LocusAnalyzer.hh" +#include "sample/GenomeMask.hh" namespace ehunter { @@ -46,23 +46,27 @@ enum class AnalyzerInputType // Stores information needed to properly pass reads to the analyzer struct AnalyzerBundle { - AnalyzerBundle(RegionType regionType, LocusAnalyzer* locusAnalyzerPtr) + AnalyzerBundle(locus::RegionType regionType, const size_t initLocusAnalyzerIndex) : regionType(regionType) , inputType(AnalyzerInputType::kBothReads) - , locusAnalyzerPtr(locusAnalyzerPtr) + , locusIndex(initLocusAnalyzerIndex) { } - RegionType regionType; + locus::RegionType regionType; AnalyzerInputType inputType; - LocusAnalyzer* locusAnalyzerPtr; + size_t locusIndex; }; +void processAnalyzerBundleReadPair( + locus::LocusAnalyzer& locusAnalyzer, locus::RegionType regionType, AnalyzerInputType inputType, Read& read, + Read& mate, graphtools::AlignerSelector& alignerSelector); + // Enables retrieval of appropriate locus analyzers by genomic coordinates of read alignments class AnalyzerFinder { public: - AnalyzerFinder(std::vector>& locusAnalyzers); + AnalyzerFinder(std::vector>& locusAnalyzers); // Retrieves analyzers appropriate for the given read pair std::vector query( @@ -73,7 +77,7 @@ public: std::vector query(int32_t readContigId, int64_t readStart, int64_t readEnd) const; private: - using AnalyzerIntervalTree = ehunter::IntervalTree; + using AnalyzerIntervalTree = IntervalTree; using AnalyzerIntervalTrees = std::unordered_map; AnalyzerIntervalTrees intervalTrees_; diff --git a/sample_analysis/GenomeMask.cpp b/ehunter/sample/GenomeMask.cpp similarity index 92% rename from sample_analysis/GenomeMask.cpp rename to ehunter/sample/GenomeMask.cpp index 1630f62..a772bc1 100644 --- a/sample_analysis/GenomeMask.cpp +++ b/ehunter/sample/GenomeMask.cpp @@ -28,11 +28,11 @@ namespace ehunter namespace { - inline size_t binPos(int64_t pos) - { - static const int binSizeLog2 = 16; - return pos >> binSizeLog2; - } +inline size_t binPos(int64_t pos) +{ + static const int binSizeLog2 = 10; + return pos >> binSizeLog2; +} } bool GenomeMask::query(int32_t contigId, int64_t pos) const diff --git a/sample_analysis/GenomeMask.hh b/ehunter/sample/GenomeMask.hh similarity index 100% rename from sample_analysis/GenomeMask.hh rename to ehunter/sample/GenomeMask.hh diff --git a/sample_analysis/GenomeQueryCollection.cpp b/ehunter/sample/GenomeQueryCollection.cpp similarity index 59% rename from sample_analysis/GenomeQueryCollection.cpp rename to ehunter/sample/GenomeQueryCollection.cpp index c2fd706..9243992 100644 --- a/sample_analysis/GenomeQueryCollection.cpp +++ b/ehunter/sample/GenomeQueryCollection.cpp @@ -19,8 +19,9 @@ // // -#include "sample_analysis/GenomeQueryCollection.hh" +#include "sample/GenomeQueryCollection.hh" +using ehunter::locus::LocusAnalyzer; using std::unique_ptr; using std::vector; @@ -29,24 +30,24 @@ namespace ehunter namespace { - void initializeGenomeMask(GenomeMask& genomeMask, vector>& locusAnalyzers) +void initializeGenomeMask(GenomeMask& genomeMask, vector>& locusAnalyzers) +{ + for (auto& locusAnalyzer : locusAnalyzers) { - for (auto& locusAnalyzer : locusAnalyzers) - { - const LocusSpecification& locusSpec = locusAnalyzer->locusSpec(); + const LocusSpecification& locusSpec = locusAnalyzer->locusSpec(); - for (const auto& region : locusSpec.targetReadExtractionRegions()) - { - genomeMask.addRegion(region.contigIndex(), region.start(), region.end()); - } + for (const auto& region : locusSpec.targetReadExtractionRegions()) + { + genomeMask.addRegion(region.contigIndex(), region.start(), region.end()); + } - for (const auto& region : locusSpec.offtargetReadExtractionRegions()) - { - genomeMask.addRegion(region.contigIndex(), region.start(), region.end()); - } + for (const auto& region : locusSpec.offtargetReadExtractionRegions()) + { + genomeMask.addRegion(region.contigIndex(), region.start(), region.end()); } } } +} GenomeQueryCollection::GenomeQueryCollection(vector>& locusAnalyzers) : analyzerFinder(locusAnalyzers) diff --git a/sample_analysis/GenomeQueryCollection.hh b/ehunter/sample/GenomeQueryCollection.hh similarity index 91% rename from sample_analysis/GenomeQueryCollection.hh rename to ehunter/sample/GenomeQueryCollection.hh index 37c5528..521745f 100644 --- a/sample_analysis/GenomeQueryCollection.hh +++ b/ehunter/sample/GenomeQueryCollection.hh @@ -30,7 +30,7 @@ namespace ehunter // Aggregates various methods for querying genome struct GenomeQueryCollection { - GenomeQueryCollection(std::vector>& locusAnalyzers); + GenomeQueryCollection(std::vector>& locusAnalyzers); AnalyzerFinder analyzerFinder; // Analyzers searchable by targeted region GenomeMask targetRegionMask; // Marks targeted regions to enable fast read screening diff --git a/ehunter/sample/HtsFileSeeker.cpp b/ehunter/sample/HtsFileSeeker.cpp new file mode 100644 index 0000000..a59b015 --- /dev/null +++ b/ehunter/sample/HtsFileSeeker.cpp @@ -0,0 +1,163 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/HtsFileSeeker.hh" + +#include +#include + +#include "core/HtsHelpers.hh" + +using std::string; + +namespace ehunter +{ + +namespace htshelpers +{ + +HtsFileSeeker::HtsFileSeeker(const string& htsFilePath, const std::string& htsReferencePath) + : htsFilePath_(htsFilePath) + , htsReferencePath_(htsReferencePath) + , contigInfo_({}) +{ + openFile(); + loadHeader(); + loadIndex(); + htsAlignmentPtr_ = bam_init1(); +} + +HtsFileSeeker::~HtsFileSeeker() +{ + bam_destroy1(htsAlignmentPtr_); + htsAlignmentPtr_ = nullptr; + + if (htsRegionPtr_) + { + hts_itr_destroy(htsRegionPtr_); + htsRegionPtr_ = nullptr; + } + + hts_idx_destroy(htsIndexPtr_); + htsIndexPtr_ = nullptr; + + bam_hdr_destroy(htsHeaderPtr_); + htsHeaderPtr_ = nullptr; + + sam_close(htsFilePtr_); + htsFilePtr_ = nullptr; +} + +void HtsFileSeeker::openFile() +{ + htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); + + if (!htsFilePtr_) + { + throw std::runtime_error("Failed to read BAM file " + htsFilePath_); + } + + // Required step for parsing of some CRAMs + if (hts_set_fai_filename(htsFilePtr_, htsReferencePath_.c_str()) != 0) + { + throw std::runtime_error("Failed to set index of: " + htsReferencePath_); + } +} + +void HtsFileSeeker::loadHeader() +{ + htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); + + if (!htsHeaderPtr_) + { + throw std::runtime_error("Failed to read header of " + htsFilePath_); + } + + contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); +} + +void HtsFileSeeker::loadIndex() +{ + htsIndexPtr_ = sam_index_load(htsFilePtr_, htsFilePath_.c_str()); + + if (!htsIndexPtr_) + { + throw std::runtime_error("Failed to read index of " + htsFilePath_); + } +} + +void HtsFileSeeker::closeRegion() +{ + if (htsRegionPtr_) + { + hts_itr_destroy(htsRegionPtr_); + htsRegionPtr_ = nullptr; + } +} + +void HtsFileSeeker::setRegion(const GenomicRegion& region) +{ + closeRegion(); + + htsRegionPtr_ = sam_itr_queryi(htsIndexPtr_, region.contigIndex(), region.start(), region.end()); + + if (htsRegionPtr_ == nullptr) + { + throw std::runtime_error("Failed to extract reads from " + encode(contigInfo_, region)); + } + + status_ = Status::kStreamingReads; +} + +bool HtsFileSeeker::trySeekingToNextPrimaryAlignment() +{ + if (status_ != Status::kStreamingReads) + { + return false; + } + + int32_t returnCode = 0; + + while ((returnCode = sam_itr_next(htsFilePtr_, htsRegionPtr_, htsAlignmentPtr_)) >= 0) + { + if (isPrimaryAlignment(htsAlignmentPtr_)) + return true; + } + + status_ = Status::kFinishedStreaming; + + if (returnCode < -1) + { + throw std::runtime_error("Failed to extract a record from " + htsFilePath_); + } + + return false; +} + +Read HtsFileSeeker::decodeRead(LinearAlignmentStats& alignmentStats) const +{ + alignmentStats = decodeAlignmentStats(htsAlignmentPtr_); + return htshelpers::decodeRead(htsAlignmentPtr_); +} + +} + +} diff --git a/ehunter/sample/HtsFileSeeker.hh b/ehunter/sample/HtsFileSeeker.hh new file mode 100644 index 0000000..c20334d --- /dev/null +++ b/ehunter/sample/HtsFileSeeker.hh @@ -0,0 +1,87 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include +#include + +#include "boost/noncopyable.hpp" +extern "C" +{ +#include "htslib/hts.h" +#include "htslib/sam.h" +} + +#include "core/GenomicRegion.hh" +#include "core/Read.hh" +#include "core/ReferenceContigInfo.hh" + +namespace ehunter +{ + +namespace htshelpers +{ + +class HtsFileSeeker : private boost::noncopyable +{ +public: + HtsFileSeeker(const std::string& htsFilePath, const std::string& htsReferencePath); + ~HtsFileSeeker(); + void setRegion(const GenomicRegion& region); + bool trySeekingToNextPrimaryAlignment(); + + int32_t currentReadChromIndex() const; + const std::string& currentReadChrom() const; + int32_t currentReadPosition() const; + int32_t currentMateChromIndex() const; + const std::string& currentMateChrom() const; + int32_t currentMatePosition() const; + + Read decodeRead(LinearAlignmentStats& alignmentStats) const; + +private: + enum class Status + { + kStreamingReads, + kFinishedStreaming + }; + + void openFile(); + void loadHeader(); + void loadIndex(); + void closeRegion(); + + const std::string htsFilePath_; + const std::string htsReferencePath_; + ReferenceContigInfo contigInfo_; + Status status_ = Status::kFinishedStreaming; + + htsFile* htsFilePtr_ = nullptr; + bam_hdr_t* htsHeaderPtr_ = nullptr; + hts_idx_t* htsIndexPtr_ = nullptr; + hts_itr_t* htsRegionPtr_ = nullptr; + bam1_t* htsAlignmentPtr_ = nullptr; +}; + +} + +} diff --git a/ehunter/sample/HtsFileStreamer.cpp b/ehunter/sample/HtsFileStreamer.cpp new file mode 100644 index 0000000..e8dd7be --- /dev/null +++ b/ehunter/sample/HtsFileStreamer.cpp @@ -0,0 +1,130 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/HtsFileStreamer.hh" + +#include "core/HtsHelpers.hh" + +using std::string; + +namespace ehunter +{ + +namespace htshelpers +{ + +void HtsFileStreamer::openHtsFile(const unsigned decompressionThreads) +{ + htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); + + if (!htsFilePtr_) + { + throw std::runtime_error("Failed to read BAM file " + htsFilePath_); + } + + // Required step for parsing of some CRAMs + if (hts_set_fai_filename(htsFilePtr_, htsReferencePath_.c_str()) != 0) + { + throw std::runtime_error("Failed to set index of: " + htsReferencePath_); + } + + /// Create thread pool for bgzf block decompression. The behavior of htslib seems to be to use this pool instead of + /// (not in addition to) the calling thread, therefore there is no point in creating a decompression thread-pool + /// with less than 2 threads. + if (decompressionThreads > 1) + { + htsThreadPool_.pool = hts_tpool_init(decompressionThreads); + if (not htsThreadPool_.pool) + { + throw std::runtime_error( + "HtsFileStreamer: Error creating htslib threadpool with " + std::to_string(decompressionThreads) + + " threads."); + } + hts_set_opt(htsFilePtr_, HTS_OPT_THREAD_POOL, &htsThreadPool_); + } +} + +void HtsFileStreamer::loadHeader() +{ + htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); + + if (!htsHeaderPtr_) + { + throw std::runtime_error("Failed to read header of " + htsFilePath_); + } + + contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); +} + +void HtsFileStreamer::prepareForStreamingAlignments() { htsAlignmentPtr_ = bam_init1(); } + +bool HtsFileStreamer::trySeekingToNextPrimaryAlignment() +{ + if (status_ != Status::kStreamingReads) + { + return false; + } + + int32_t returnCode = 0; + + while ((returnCode = sam_read1(htsFilePtr_, htsHeaderPtr_, htsAlignmentPtr_)) >= 0) + { + if (isPrimaryAlignment(htsAlignmentPtr_)) + return true; + } + + status_ = Status::kFinishedStreaming; + + if (returnCode < -1) + { + throw std::runtime_error("Failed to extract a record from " + htsFilePath_); + } + + return false; +} + +bool HtsFileStreamer::isStreamingAlignedReads() const +{ + return status_ != Status::kFinishedStreaming && currentReadContigId() != -1; +} + +Read HtsFileStreamer::decodeRead() const { return htshelpers::decodeRead(htsAlignmentPtr_); } + +HtsFileStreamer::~HtsFileStreamer() +{ + bam_destroy1(htsAlignmentPtr_); + htsAlignmentPtr_ = nullptr; + + bam_hdr_destroy(htsHeaderPtr_); + htsHeaderPtr_ = nullptr; + + sam_close(htsFilePtr_); + htsFilePtr_ = nullptr; + + if (htsThreadPool_.pool) + { + hts_tpool_destroy(htsThreadPool_.pool); + } +} + +} + +} diff --git a/ehunter/sample/HtsFileStreamer.hh b/ehunter/sample/HtsFileStreamer.hh new file mode 100644 index 0000000..ab521ce --- /dev/null +++ b/ehunter/sample/HtsFileStreamer.hh @@ -0,0 +1,98 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include +#include + +extern "C" +{ +#include "htslib/hts.h" +#include "htslib/sam.h" +} + +#include "core/Read.hh" +#include "core/ReferenceContigInfo.hh" + +namespace ehunter +{ + +namespace htshelpers +{ + +class HtsFileStreamer +{ +public: + /// \param[in] decompressionThreads Total size of thread pool used for bgzip decompression of the hts file. Has no + /// effect if the file is uncompressed. When set to one or less the calling thread handles all decompression and no + /// thread pool is used. + /// + HtsFileStreamer( + const std::string& htsFilePath, const std::string& htsReferencePath, const unsigned decompressionThreads = 1) + : htsFilePath_(htsFilePath) + , htsReferencePath_(htsReferencePath) + , contigInfo_({}) + { + openHtsFile(decompressionThreads); + loadHeader(); + prepareForStreamingAlignments(); + } + ~HtsFileStreamer(); + + bool trySeekingToNextPrimaryAlignment(); + + int32_t currentReadContigId() const { return htsAlignmentPtr_->core.tid; } + hts_pos_t currentReadPosition() const { return htsAlignmentPtr_->core.pos; } + int32_t currentReadLength() const; + int32_t currentMateContigId() const { return htsAlignmentPtr_->core.mtid; } + hts_pos_t currentMatePosition() const { return htsAlignmentPtr_->core.mpos; } + + bool currentIsPaired() const { return (htsAlignmentPtr_->core.flag & BAM_FPAIRED); } + + bool isStreamingAlignedReads() const; + + Read decodeRead() const; + +private: + enum class Status + { + kStreamingReads, + kFinishedStreaming + }; + + void openHtsFile(unsigned decompressionThreads); + void loadHeader(); + void prepareForStreamingAlignments(); + + const std::string htsFilePath_; + const std::string htsReferencePath_; + ReferenceContigInfo contigInfo_; + Status status_ = Status::kStreamingReads; + + htsFile* htsFilePtr_ = nullptr; + bam1_t* htsAlignmentPtr_ = nullptr; + bam_hdr_t* htsHeaderPtr_ = nullptr; + htsThreadPool htsThreadPool_ = { nullptr, 0 }; +}; + +} +} diff --git a/ehunter/sample/HtsSeekingSampleAnalysis.cpp b/ehunter/sample/HtsSeekingSampleAnalysis.cpp new file mode 100644 index 0000000..6f2a73e --- /dev/null +++ b/ehunter/sample/HtsSeekingSampleAnalysis.cpp @@ -0,0 +1,374 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko , +// Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/HtsSeekingSampleAnalysis.hh" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// clang-format off +// Note that spdlog.h must be included before ostr.h +#include "spdlog/spdlog.h" +#include "spdlog/fmt/ostr.h" +// clang-format on + +#include "core/ReadPairs.hh" +#include "locus/LocusAnalyzer.hh" +#include "sample/AnalyzerFinder.hh" +#include "sample/HtsFileSeeker.hh" +#include "sample/IndexBasedDepthEstimate.hh" +#include "sample/MateExtractor.hh" + +using boost::make_unique; +using boost::optional; +using ehunter::htshelpers::HtsFileSeeker; +using ehunter::locus::LocusAnalyzer; +using graphtools::AlignmentWriter; +using std::ostream; +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace ehunter +{ + +namespace +{ +using AlignmentStatsCatalog = unordered_map>; + +vector +combineRegions(const vector& targetRegions, const vector& offtargetRegions) +{ + vector combinedRegions(targetRegions); + combinedRegions.insert(combinedRegions.end(), offtargetRegions.begin(), offtargetRegions.end()); + return combinedRegions; +} + +bool checkIfMatesWereMappedNearby(const LinearAlignmentStats& alignmentStats) +{ + const int kMaxMateDistance = 1000; + if ((alignmentStats.chromId == alignmentStats.mateChromId) + && (std::abs(alignmentStats.pos - alignmentStats.matePos) < kMaxMateDistance)) + { + return true; + } + return false; +} + +void recoverMates( + htshelpers::MateExtractor& mateExtractor, AlignmentStatsCatalog& alignmentStatsCatalog, ReadPairs& readPairs) +{ + for (auto& fragmentIdAndReadPair : readPairs) + { + ReadPair& readPair = fragmentIdAndReadPair.second; + + if (readPair.numMatesSet() == 2) + { + continue; + } + + const Read& read = readPair.firstMate ? *readPair.firstMate : *readPair.secondMate; + + const auto alignmentStatsIterator = alignmentStatsCatalog.find(read.readId()); + if (alignmentStatsIterator == alignmentStatsCatalog.end()) + { + throw std::logic_error("Cannot recover mate of uncatalogued read"); + } + const LinearAlignmentStats& alignmentStats = alignmentStatsIterator->second; + + if (!checkIfMatesWereMappedNearby(alignmentStats)) + { + LinearAlignmentStats mateStats; + optional optionalMate = mateExtractor.extractMate(read, alignmentStats, mateStats); + if (optionalMate) + { + const Read& mate = *optionalMate; + alignmentStatsCatalog.emplace(std::make_pair(mate.readId(), alignmentStats)); + readPairs.AddMateToExistingRead(mate); + } + else + { + spdlog::warn("Could not recover the mate of {}", read.readId()); + } + } + } +} + +ReadPairs collectCandidateReads( + const vector& targetRegions, const vector& offtargetRegions, + AlignmentStatsCatalog& alignmentStatsCatalog, HtsFileSeeker& htsFileSeeker, + htshelpers::MateExtractor& mateExtractor) +{ + vector regionsWithReads = combineRegions(targetRegions, offtargetRegions); + ReadPairs readPairs; + + for (const auto& regionWithReads : regionsWithReads) + { + const int numReadsBeforeCollection = readPairs.NumReads(); + htsFileSeeker.setRegion(regionWithReads); + while (htsFileSeeker.trySeekingToNextPrimaryAlignment()) + { + LinearAlignmentStats alignmentStats; + Read read = htsFileSeeker.decodeRead(alignmentStats); + if (alignmentStats.isPaired) + { + alignmentStatsCatalog.emplace(std::make_pair(read.readId(), alignmentStats)); + readPairs.Add(std::move(read)); + } + else + { + spdlog::warn("Skipping {} because it is unpaired", read.readId()); + } + } + const int numReadsCollected = readPairs.NumReads() - numReadsBeforeCollection; + spdlog::debug("Collected {} reads from {}", numReadsCollected, regionWithReads); + } + + const int numReadsBeforeRecovery = readPairs.NumReads(); + recoverMates(mateExtractor, alignmentStatsCatalog, readPairs); + const int numReadsAfterRecovery = readPairs.NumReads() - numReadsBeforeRecovery; + spdlog::debug("Recovered {} reads", numReadsAfterRecovery); + + return readPairs; +} + +void analyzeReadPair( + vector>& locusAnalyzers, AnalyzerFinder& analyzerFinder, Read& read, Read& mate, + const AlignmentStatsCatalog& alignmentStats, graphtools::AlignerSelector& alignerSelector) +{ + const auto readStatsIter = alignmentStats.find(read.readId()); + const auto mateStatsIter = alignmentStats.find(mate.readId()); + + if (readStatsIter == alignmentStats.end() || mateStatsIter == alignmentStats.end()) + { + throw std::logic_error("Could not to find alignment stats for " + read.fragmentId()); + } + + const LinearAlignmentStats& readStats = readStatsIter->second; + const LinearAlignmentStats& mateStats = mateStatsIter->second; + + const int64_t readEnd = readStats.pos + read.sequence().length(); + const int64_t mateEnd = mateStats.pos + mate.sequence().length(); + vector analyzers + = analyzerFinder.query(readStats.chromId, readStats.pos, readEnd, mateStats.chromId, mateStats.pos, mateEnd); + + if (analyzers.empty()) + { + return; + } + + assert(analyzers.size() == 1); + auto& analyzer(analyzers.front()); + processAnalyzerBundleReadPair( + *locusAnalyzers[analyzer.locusIndex], analyzer.regionType, analyzer.inputType, read, mate, alignerSelector); +} + +void analyzeRead( + vector>& locusAnalyzers, AnalyzerFinder& analyzerFinder, Read& read, + const AlignmentStatsCatalog& alignmentStats, graphtools::AlignerSelector& alignerSelector) +{ + const auto readStatsIter = alignmentStats.find(read.readId()); + + if (readStatsIter == alignmentStats.end()) + { + throw std::logic_error("Could not to find alignment stats for " + read.fragmentId()); + } + + const LinearAlignmentStats& readStats = readStatsIter->second; + const int64_t readEnd = readStats.pos + read.sequence().length(); + + vector analyzers = analyzerFinder.query(readStats.chromId, readStats.pos, readEnd); + + if (analyzers.empty()) + { + return; + } + + assert(analyzers.size() == 1); + auto& analyzer(analyzers.front()); + locusAnalyzers[analyzer.locusIndex]->processMates(read, nullptr, analyzer.regionType, alignerSelector); +} + +void processReads( + vector>& locusAnalyzers, ReadPairs& candidateReadPairs, + const AlignmentStatsCatalog& alignmentStats, AnalyzerFinder& analyzerFinder, + graphtools::AlignerSelector& alignerSelector) +{ + for (auto& fragmentIdAndReads : candidateReadPairs) + { + auto& readPair = fragmentIdAndReads.second; + if (readPair.numMatesSet() == 2) + { + analyzeReadPair( + locusAnalyzers, analyzerFinder, *readPair.firstMate, *readPair.secondMate, alignmentStats, + alignerSelector); + } + else + { + Read& read = readPair.firstMate ? *readPair.firstMate : *readPair.secondMate; + analyzeRead(locusAnalyzers, analyzerFinder, read, alignmentStats, alignerSelector); + } + } +} + +/// \brief Mutable data shared by all worker threads +/// +class LocusThreadSharedData +{ +public: + LocusThreadSharedData() + : isWorkerThreadException(false) + , locusIndex(0) + { + } + + std::atomic isWorkerThreadException; + std::atomic locusIndex; +}; + +/// \brief Data isolated to each locus-processing thread +/// +struct LocusThreadLocalData +{ + std::exception_ptr threadExceptionPtr = nullptr; +}; + +/// \brief Process a series of loci on one thread +/// +void processLocus( + const int threadIndex, const InputPaths& inputPaths, const Sex sampleSex, + const HeuristicParameters& heuristicParams, const RegionCatalog& regionCatalog, + locus::AlignWriterPtr alignmentWriter, SampleFindings& sampleFindings, LocusThreadSharedData& locusThreadSharedData, + std::vector& locusThreadLocalDataPool) +{ + LocusThreadLocalData& locusThreadData(locusThreadLocalDataPool[threadIndex]); + std::string locusId = "Unknown"; + + try + { + HtsFileSeeker htsFileSeeker(inputPaths.htsFile(), inputPaths.reference()); + htshelpers::MateExtractor mateExtractor(inputPaths.htsFile(), inputPaths.reference()); + graphtools::AlignerSelector alignerSelector(heuristicParams.alignerType()); + + const unsigned size(regionCatalog.size()); + while (true) + { + if (locusThreadSharedData.isWorkerThreadException.load()) + { + return; + } + const auto locusIndex(locusThreadSharedData.locusIndex.fetch_add(1)); + if (locusIndex >= size) + { + return; + } + + const auto& locusSpec(regionCatalog[locusIndex]); + locusId = locusSpec.locusId(); + + spdlog::info("Analyzing {}", locusId); + vector> locusAnalyzers; + auto analyzer(make_unique(locusSpec, heuristicParams, alignmentWriter)); + locusAnalyzers.emplace_back(std::move(analyzer)); + AnalyzerFinder analyzerFinder(locusAnalyzers); + + AlignmentStatsCatalog alignmentStats; + ReadPairs readPairs = collectCandidateReads( + locusSpec.targetReadExtractionRegions(), locusSpec.offtargetReadExtractionRegions(), alignmentStats, + htsFileSeeker, mateExtractor); + + processReads(locusAnalyzers, readPairs, alignmentStats, analyzerFinder, alignerSelector); + + sampleFindings[locusIndex] = locusAnalyzers.front()->analyze(sampleSex, boost::none); + } + } + catch (const std::exception& e) + { + locusThreadSharedData.isWorkerThreadException = true; + locusThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error("Exception caught in thread {} while processing locus: {} : {}", threadIndex, locusId, e.what()); + throw; + } + catch (...) + { + locusThreadSharedData.isWorkerThreadException = true; + locusThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error("Unknown exception caught in thread {} while processing locus: {}", threadIndex, locusId); + throw; + } +} +} + +SampleFindings htsSeekingSampleAnalysis( + const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, const int threadCount, + const RegionCatalog& regionCatalog, locus::AlignWriterPtr alignmentWriter) +{ + LocusThreadSharedData locusThreadSharedData; + std::vector locusThreadLocalDataPool(threadCount); + + const unsigned locusCount(regionCatalog.size()); + SampleFindings sampleFindings(locusCount); + + // Start all locus worker threads + std::vector locusThreads; + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + locusThreads.emplace_back( + processLocus, threadIndex, std::cref(inputPaths), sampleSex, std::cref(heuristicParams), + std::cref(regionCatalog), alignmentWriter, std::ref(sampleFindings), std::ref(locusThreadSharedData), + std::ref(locusThreadLocalDataPool)); + } + + // Rethrow exceptions from worker pool in thread order: + if (locusThreadSharedData.isWorkerThreadException.load()) + { + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + const auto& locusThreadData(locusThreadLocalDataPool[threadIndex]); + if (locusThreadData.threadExceptionPtr) + { + std::rethrow_exception(locusThreadData.threadExceptionPtr); + } + } + } + + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + locusThreads[threadIndex].join(); + } + + return sampleFindings; +} + +} diff --git a/sample_analysis/HtsSeekingSampleAnalysis.hh b/ehunter/sample/HtsSeekingSampleAnalysis.hh similarity index 77% rename from sample_analysis/HtsSeekingSampleAnalysis.hh rename to ehunter/sample/HtsSeekingSampleAnalysis.hh index b3c5fff..c5db30a 100644 --- a/sample_analysis/HtsSeekingSampleAnalysis.hh +++ b/ehunter/sample/HtsSeekingSampleAnalysis.hh @@ -26,15 +26,16 @@ #include "graphio/AlignmentWriter.hh" -#include "common/Parameters.hh" -#include "region_analysis/LocusFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Parameters.hh" +#include "locus/LocusAnalyzer.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { SampleFindings htsSeekingSampleAnalysis( - const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, - const RegionCatalog& regionCatalog, graphtools::AlignmentWriter& alignmentWriter); + const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, int threadCount, + const RegionCatalog& regionCatalog, locus::AlignWriterPtr alignmentWriter); } diff --git a/ehunter/sample/HtsStreamingReadPairQueue.cpp b/ehunter/sample/HtsStreamingReadPairQueue.cpp new file mode 100644 index 0000000..c4dba4f --- /dev/null +++ b/ehunter/sample/HtsStreamingReadPairQueue.cpp @@ -0,0 +1,72 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/HtsStreamingReadPairQueue.hh" + +namespace ehunter +{ + +bool HtsStreamingReadPairQueue::insertReadPair(const unsigned locusIndex, ReadPair readPair) +{ + auto& locusAnalyzerQueue(queues_[locusIndex]); + std::unique_lock locusAnalyzerQueueLock(locusAnalyzerQueue.mutex); + const bool wasInActive(not locusAnalyzerQueue.isActive); + if (wasInActive) + { + std::unique_lock globalLock(mutex_); + while (activeLocusAnalyzerQueues_ >= maxActiveLocusAnalyzerQueues_) + { + cv_.wait(globalLock); + } + activeLocusAnalyzerQueues_++; + globalLock.unlock(); + + locusAnalyzerQueue.isActive = true; + } + locusAnalyzerQueue.queue.emplace(std::move(readPair)); + return wasInActive; +} + +void HtsStreamingReadPairQueue::getNextReadPair(const unsigned locusIndex, boost::optional& readPair) +{ + auto& locusAnalyzerQueue(queues_[locusIndex]); + std::unique_lock locusAnalyzerQueueLock(locusAnalyzerQueue.mutex); + + if (locusAnalyzerQueue.queue.empty()) + { + std::unique_lock globalLock(mutex_); + activeLocusAnalyzerQueues_--; + globalLock.unlock(); + + locusAnalyzerQueue.isActive = false; + locusAnalyzerQueueLock.unlock(); + + cv_.notify_one(); + readPair = boost::none; + } + else + { + readPair = std::move(locusAnalyzerQueue.queue.front()); + locusAnalyzerQueue.queue.pop(); + } +} + +} diff --git a/ehunter/sample/HtsStreamingReadPairQueue.hh b/ehunter/sample/HtsStreamingReadPairQueue.hh new file mode 100644 index 0000000..217cd72 --- /dev/null +++ b/ehunter/sample/HtsStreamingReadPairQueue.hh @@ -0,0 +1,106 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#pragma once + +#include +#include +#include + +#include "boost/optional.hpp" + +#include "core/Read.hh" +#include "locus/LocusAnalyzer.hh" +#include "sample/AnalyzerFinder.hh" + +namespace ehunter +{ + +/// \brief Custom concurrent queue manager for EH streaming mode. +/// +/// THe parallelization strategy used by EH streaming mode has a constraint to have no more than one thread operating on +/// each LocusAnalyzer at a time. This object assists by holding a queue of work items (ReadPairs) for each +/// LocusAnalyzer, managing parallel read/write requests to each queue, and limiting the total number of queues which +/// will be saved. +/// +class HtsStreamingReadPairQueue +{ +public: + /// \param[in] maxActiveLocusAnalyzerQueues The max number of non-empty LocusAnalyzer queues to store before + /// blocking additional input + /// + HtsStreamingReadPairQueue(const unsigned maxActiveLocusAnalyzerQueues, const unsigned locusAnalyzerCount) + : maxActiveLocusAnalyzerQueues_(maxActiveLocusAnalyzerQueues) + , activeLocusAnalyzerQueues_(0) + , queues_(locusAnalyzerCount) + { + } + + struct ReadPair + { + ReadPair(const ReadPair&) = delete; + ReadPair& operator=(const ReadPair&) = delete; + ReadPair(ReadPair&& other) = default; + ReadPair& operator=(ReadPair&& other) = default; + + locus::RegionType regionType; + AnalyzerInputType inputType; + Read read; + Read mate; + }; + + /// \brief Insert a new read pair into the \p locusIndex queue + /// + /// If \p locusIndex corresponds to an inactive queue, this will block until the queue can be activated without + /// exceeding maxActiveLocusAnalyzerQueues. + /// + /// \return True if the locusAnalyzer at \p locusIndex was marked as inactive before this method call + /// + bool insertReadPair(unsigned locusIndex, ReadPair readPair); + + /// \brief Retrieve a read pair from the \p locusIndex queue + /// + /// \param[out] readPair Next read pair enqueued for \p locusIndex, or none if the queue is empty + /// + void getNextReadPair(unsigned locusIndex, boost::optional& readPair); + +private: + struct LocusAnalyzerQueue + { + std::queue queue; + + /// Queue mutex is used to synchronize between the two queue interaction threads (reader and writer) + std::mutex mutex; + + /// True if a thread is either processing or scheduled to process this queue already + bool isActive = false; + }; + + const unsigned maxActiveLocusAnalyzerQueues_; + unsigned activeLocusAnalyzerQueues_; + std::vector queues_; + + /// Global mutex/cv are used to enforce the max active LocusAnalyzerQueue limit + std::mutex mutex_; + std::condition_variable cv_; +}; + +} \ No newline at end of file diff --git a/ehunter/sample/HtsStreamingSampleAnalysis.cpp b/ehunter/sample/HtsStreamingSampleAnalysis.cpp new file mode 100644 index 0000000..2e5824f --- /dev/null +++ b/ehunter/sample/HtsStreamingSampleAnalysis.cpp @@ -0,0 +1,360 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko , +// Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/HtsStreamingSampleAnalysis.hh" + +#include + +#include "absl/container/flat_hash_set.h" +#include "spdlog/spdlog.h" +#include + +#include "core/HtsHelpers.hh" +#include "core/ThreadPool.hh" +#include "locus/LocusAnalyzer.hh" +#include "locus/LocusAnalyzerUtil.hh" +#include "sample/GenomeQueryCollection.hh" +#include "sample/HtsFileStreamer.hh" +#include "sample/HtsStreamingReadPairQueue.hh" + +using ehunter::locus::initializeLocusAnalyzers; +using ehunter::locus::LocusAnalyzer; +using graphtools::AlignmentWriter; +using std::string; +using std::vector; + +namespace ehunter +{ + +namespace +{ + +/// \brief Mutable data shared by all LocusAnalyzer-processing threads +/// +class LocusAnalyzerThreadSharedData +{ +public: + LocusAnalyzerThreadSharedData(const unsigned maxActiveLocusAnalyzerQueues, const unsigned locusAnalyzerCount) + : isWorkerThreadException(false) + , readPairQueue(maxActiveLocusAnalyzerQueues, locusAnalyzerCount) + { + } + + std::atomic isWorkerThreadException; + HtsStreamingReadPairQueue readPairQueue; + vector> locusAnalyzers; +}; + +/// \brief Data isolated to each LocusAnalyzer-processing thread +/// +struct LocusAnalyzerThreadLocalData +{ + std::exception_ptr threadExceptionPtr = nullptr; + std::shared_ptr alignerSelectorPtr; +}; + +/// Process queue for a single LocusAnalyzer on one thread +void processLocusAnalyzerQueue( + const int threadIndex, LocusAnalyzerThreadSharedData& locusAnalyzerThreadSharedData, + std::vector& locusAnalyzerThreadLocalDataPool, const unsigned locusIndex) +{ + if (locusAnalyzerThreadSharedData.isWorkerThreadException.load()) + { + return; + } + + LocusAnalyzerThreadLocalData& locusAnalyzerThreadData(locusAnalyzerThreadLocalDataPool[threadIndex]); + auto& locusAnalyzer(*locusAnalyzerThreadSharedData.locusAnalyzers[locusIndex]); + + boost::optional readPair; + + try + { + while (true) + { + locusAnalyzerThreadSharedData.readPairQueue.getNextReadPair(locusIndex, readPair); + if (not readPair) + { + break; + } + processAnalyzerBundleReadPair( + locusAnalyzer, readPair->regionType, readPair->inputType, readPair->read, readPair->mate, + *locusAnalyzerThreadData.alignerSelectorPtr); + } + } + catch (const std::exception& e) + { + locusAnalyzerThreadSharedData.isWorkerThreadException.store(true); + locusAnalyzerThreadData.threadExceptionPtr = std::current_exception(); + + std::ostringstream oss; + oss << "Exception caught in thread " << threadIndex << " while processing read pair queue for locus: `" + << locusAnalyzer.locusId() << "`"; + if (readPair) + { + oss << " current readPair: `" << readPair->read.fragmentId() << "`"; + } + oss << ": " << e.what(); + spdlog::error(oss.str()); + throw; + } + catch (...) + { + locusAnalyzerThreadSharedData.isWorkerThreadException.store(true); + locusAnalyzerThreadData.threadExceptionPtr = std::current_exception(); + + std::ostringstream oss; + oss << "Exception caught in thread " << threadIndex << " while processing read pair queue for locus: `" + << locusAnalyzer.locusId() << "`"; + if (readPair) + { + oss << " current readPair: `" << readPair->read.fragmentId() << "`"; + } + spdlog::error(oss.str()); + throw; + } +} + +/// \brief Mutable data shared by all SampleFindings-processing threads +/// +class SampleFindingsThreadSharedData +{ +public: + SampleFindingsThreadSharedData() + : isWorkerThreadException(false) + , locusIndex(0) + { + } + + std::atomic isWorkerThreadException; + std::atomic locusIndex; +}; + +/// \brief Data isolated to each SampleFindings-processing thread +/// +struct SampleFindingsThreadLocalData +{ + std::exception_ptr threadExceptionPtr = nullptr; +}; + +/// \brief Analyze a series of loci on one thread +/// +void analyzeLocus( + const int threadIndex, const Sex sampleSex, vector>& locusAnalyzers, + SampleFindings& sampleFindings, SampleFindingsThreadSharedData& sampleFindingsThreadSharedData, + std::vector& sampleFindingsThreadLocalData) +{ + SampleFindingsThreadLocalData& sampleFindingsThreadData(sampleFindingsThreadLocalData[threadIndex]); + std::string locusId = "Unknown"; + + try + { + + const unsigned size(locusAnalyzers.size()); + while (true) + { + if (sampleFindingsThreadSharedData.isWorkerThreadException.load()) + { + return; + } + const auto locusIndex(sampleFindingsThreadSharedData.locusIndex.fetch_add(1)); + if (locusIndex >= size) + { + return; + } + + auto& locusAnalyzer(*locusAnalyzers[locusIndex]); + locusId = locusAnalyzer.locusId(); + sampleFindings[locusIndex] = locusAnalyzer.analyze(sampleSex, boost::none); + } + } + catch (const std::exception& e) + { + sampleFindingsThreadSharedData.isWorkerThreadException = true; + sampleFindingsThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error("Exception caught in thread {} while analyzing locus: {} : {}", threadIndex, locusId, e.what()); + throw; + } + catch (...) + { + sampleFindingsThreadSharedData.isWorkerThreadException = true; + sampleFindingsThreadData.threadExceptionPtr = std::current_exception(); + + spdlog::error("Unknown exception caught in thread {} while analyzing locus: {}", threadIndex, locusId); + throw; + } +} + +} + +SampleFindings htsStreamingSampleAnalysis( + const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, const int threadCount, + const RegionCatalog& regionCatalog, locus::AlignWriterPtr bamletWriter) +{ + // Setup thread-specific data structures and thread pool + const unsigned maxActiveLocusAnalyzerQueues(threadCount + 5); + const unsigned locusAnalyzerCount(regionCatalog.size()); + LocusAnalyzerThreadSharedData locusAnalyzerThreadSharedData(maxActiveLocusAnalyzerQueues, locusAnalyzerCount); + std::vector locusAnalyzerThreadLocalDataPool(threadCount); + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + auto& locusAnalyzerThreadData(locusAnalyzerThreadLocalDataPool[threadIndex]); + locusAnalyzerThreadData.alignerSelectorPtr.reset( + new graphtools::AlignerSelector(heuristicParams.alignerType())); + } + ctpl::thread_pool pool(threadCount); + + spdlog::info("Initializing all loci"); + graphtools::AlignerSelector alignerSelector(heuristicParams.alignerType()); + locusAnalyzerThreadSharedData.locusAnalyzers + = initializeLocusAnalyzers(regionCatalog, heuristicParams, bamletWriter, threadCount); + GenomeQueryCollection genomeQuery(locusAnalyzerThreadSharedData.locusAnalyzers); + + spdlog::info("Streaming reads"); + + auto ReadHash = [](const Read& read) { return std::hash()(read.fragmentId()); }; + auto ReadEq = [](const Read& read1, const Read& read2) { return (read1.fragmentId() == read2.fragmentId()); }; + using ReadCatalog = absl::flat_hash_set; + ReadCatalog unpairedReads(1000, ReadHash, ReadEq); + + const unsigned htsDecompressionThreads(std::min(threadCount, 12)); + htshelpers::HtsFileStreamer readStreamer(inputPaths.htsFile(), inputPaths.reference(), htsDecompressionThreads); + while (readStreamer.trySeekingToNextPrimaryAlignment() && readStreamer.isStreamingAlignedReads()) + { + // Stop processing reads if an exception is thrown in the worker pool: + if (locusAnalyzerThreadSharedData.isWorkerThreadException.load()) + { + break; + } + + const bool isReadNearTargetRegion = genomeQuery.targetRegionMask.query( + readStreamer.currentReadContigId(), readStreamer.currentReadPosition()); + const bool isMateNearTargetRegion = genomeQuery.targetRegionMask.query( + readStreamer.currentMateContigId(), readStreamer.currentMatePosition()); + if (!isReadNearTargetRegion && !isMateNearTargetRegion) + { + continue; + } + + if (not readStreamer.currentIsPaired()) + { + continue; + } + + Read read = readStreamer.decodeRead(); + const auto mateIterator = unpairedReads.find(read); + if (mateIterator == unpairedReads.end()) + { + unpairedReads.emplace(std::move(read)); + continue; + } + Read mate = std::move(*mateIterator); + unpairedReads.erase(mateIterator); + + const int64_t readEnd = readStreamer.currentReadPosition() + read.sequence().length(); + const int64_t mateEnd = readStreamer.currentMatePosition() + mate.sequence().length(); + + vector analyzerBundles = genomeQuery.analyzerFinder.query( + readStreamer.currentReadContigId(), readStreamer.currentReadPosition(), readEnd, + readStreamer.currentMateContigId(), readStreamer.currentMatePosition(), mateEnd); + + const unsigned bundleCount(analyzerBundles.size()); + for (unsigned bundleIndex(0); bundleIndex < bundleCount; ++bundleIndex) + { + auto& bundle(analyzerBundles[bundleIndex]); + auto sendReadPair = [&](HtsStreamingReadPairQueue::ReadPair readPair) + { + if (locusAnalyzerThreadSharedData.readPairQueue.insertReadPair(bundle.locusIndex, std::move(readPair))) + { + pool.push( + processLocusAnalyzerQueue, std::ref(locusAnalyzerThreadSharedData), + std::ref(locusAnalyzerThreadLocalDataPool), bundle.locusIndex); + } + }; + + if ((bundleIndex + 1) < bundleCount) + { + sendReadPair({ bundle.regionType, bundle.inputType, read, mate }); + } + else + { + sendReadPair({ bundle.regionType, bundle.inputType, std::move(read), std::move(mate) }); + } + } + } + + pool.stop(true); + + // Rethrow exceptions from the pool in thread order: + if (locusAnalyzerThreadSharedData.isWorkerThreadException.load()) + { + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + const LocusAnalyzerThreadLocalData& locusAnalyzerThreadData(locusAnalyzerThreadLocalDataPool[threadIndex]); + if (locusAnalyzerThreadData.threadExceptionPtr) + { + std::rethrow_exception(locusAnalyzerThreadData.threadExceptionPtr); + } + } + } + + spdlog::info("Analyzing read evidence"); + + SampleFindingsThreadSharedData sampleFindingsThreadSharedData; + std::vector sampleFindingsThreadLocalDataPool(threadCount); + + const unsigned locusCount(locusAnalyzerThreadSharedData.locusAnalyzers.size()); + SampleFindings sampleFindings(locusCount); + + // Start all sampleFindings worker threads + std::vector sampleFindingsThreads; + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + sampleFindingsThreads.emplace_back( + analyzeLocus, threadIndex, sampleSex, std::ref(locusAnalyzerThreadSharedData.locusAnalyzers), + std::ref(sampleFindings), std::ref(sampleFindingsThreadSharedData), + std::ref(sampleFindingsThreadLocalDataPool)); + } + + // Rethrow exceptions from worker pool in thread order: + if (sampleFindingsThreadSharedData.isWorkerThreadException.load()) + { + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + const auto& sampleFindingsThreadData(sampleFindingsThreadLocalDataPool[threadIndex]); + if (sampleFindingsThreadData.threadExceptionPtr) + { + std::rethrow_exception(sampleFindingsThreadData.threadExceptionPtr); + } + } + } + + for (int threadIndex(0); threadIndex < threadCount; ++threadIndex) + { + sampleFindingsThreads[threadIndex].join(); + } + + return sampleFindings; +} + +} diff --git a/sample_analysis/HtsStreamingSampleAnalysis.hh b/ehunter/sample/HtsStreamingSampleAnalysis.hh similarity index 77% rename from sample_analysis/HtsStreamingSampleAnalysis.hh rename to ehunter/sample/HtsStreamingSampleAnalysis.hh index 2f0b0c3..d5ad3d8 100644 --- a/sample_analysis/HtsStreamingSampleAnalysis.hh +++ b/ehunter/sample/HtsStreamingSampleAnalysis.hh @@ -27,15 +27,16 @@ #include "graphio/AlignmentWriter.hh" -#include "common/Parameters.hh" -#include "region_analysis/LocusFindings.hh" -#include "region_spec/LocusSpecification.hh" +#include "core/Parameters.hh" +#include "locus/LocusAnalyzer.hh" +#include "locus/LocusFindings.hh" +#include "locus/LocusSpecification.hh" namespace ehunter { SampleFindings htsStreamingSampleAnalysis( - const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, - const RegionCatalog& regionCatalog, graphtools::AlignmentWriter& alignmentWriter); + const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, const int threadCount, + const RegionCatalog& regionCatalog, locus::AlignWriterPtr alignmentWriter); } diff --git a/sample_analysis/IndexBasedDepthEstimate.cpp b/ehunter/sample/IndexBasedDepthEstimate.cpp similarity index 97% rename from sample_analysis/IndexBasedDepthEstimate.cpp rename to ehunter/sample/IndexBasedDepthEstimate.cpp index b0b1943..3ddf58c 100644 --- a/sample_analysis/IndexBasedDepthEstimate.cpp +++ b/ehunter/sample/IndexBasedDepthEstimate.cpp @@ -19,7 +19,7 @@ // // -#include "sample_analysis/IndexBasedDepthEstimate.hh" +#include "sample/IndexBasedDepthEstimate.hh" #include #include @@ -35,7 +35,7 @@ extern "C" #include "htslib/sam.h" } -#include "common/HtsHelpers.hh" +#include "core/HtsHelpers.hh" using std::string; using std::unordered_set; diff --git a/sample_analysis/IndexBasedDepthEstimate.hh b/ehunter/sample/IndexBasedDepthEstimate.hh similarity index 100% rename from sample_analysis/IndexBasedDepthEstimate.hh rename to ehunter/sample/IndexBasedDepthEstimate.hh diff --git a/ehunter/sample/MateExtractor.cpp b/ehunter/sample/MateExtractor.cpp new file mode 100644 index 0000000..eb40bac --- /dev/null +++ b/ehunter/sample/MateExtractor.cpp @@ -0,0 +1,150 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "sample/MateExtractor.hh" + +#include + +#include "core/HtsHelpers.hh" + +namespace ehunter +{ + +using boost::optional; +using std::string; + +namespace htshelpers +{ +MateExtractor::MateExtractor(const string& htsFilePath, const std::string& htsReferencePath) + : htsFilePath_(htsFilePath) + , htsReferencePath_(htsReferencePath) + , contigInfo_({}) +{ + openFile(); + loadHeader(); + loadIndex(); + htsAlignmentPtr_ = bam_init1(); +} + +MateExtractor::~MateExtractor() +{ + bam_destroy1(htsAlignmentPtr_); + htsAlignmentPtr_ = nullptr; + + hts_idx_destroy(htsIndexPtr_); + htsIndexPtr_ = nullptr; + + bam_hdr_destroy(htsHeaderPtr_); + htsHeaderPtr_ = nullptr; + + sam_close(htsFilePtr_); + htsFilePtr_ = nullptr; +} + +void MateExtractor::openFile() +{ + htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); + + if (!htsFilePtr_) + { + throw std::runtime_error("Failed to read BAM file " + htsFilePath_); + } + + // Required step for parsing of some CRAMs + if (hts_set_fai_filename(htsFilePtr_, htsReferencePath_.c_str()) != 0) + { + throw std::runtime_error("Failed to set index of: " + htsReferencePath_); + } +} + +void MateExtractor::loadHeader() +{ + htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); + + if (!htsHeaderPtr_) + { + throw std::runtime_error("Failed to read header of " + htsFilePath_); + } + + contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); +} + +void MateExtractor::loadIndex() +{ + htsIndexPtr_ = sam_index_load(htsFilePtr_, htsFilePath_.c_str()); + + if (!htsIndexPtr_) + { + throw std::runtime_error("Failed to read index of " + htsFilePath_); + } +} + +optional MateExtractor::extractMate( + const Read& read, const LinearAlignmentStats& alignmentStats, LinearAlignmentStats& mateStats) +{ + const int32_t searchRegionContigIndex + = alignmentStats.isMateMapped ? alignmentStats.mateChromId : alignmentStats.chromId; + + const int32_t searchRegionStart = alignmentStats.isMateMapped ? alignmentStats.matePos : alignmentStats.pos; + const int32_t searchRegionEnd = searchRegionStart + 1; + + hts_itr_t* htsRegionPtr_ + = sam_itr_queryi(htsIndexPtr_, searchRegionContigIndex, searchRegionStart, searchRegionEnd); + + if (!htsRegionPtr_) + { + const string& contigName = contigInfo_.getContigName(searchRegionContigIndex); + const string regionEncoding + = contigName + ":" + std::to_string(searchRegionStart) + "-" + std::to_string(searchRegionEnd); + + throw std::logic_error("Unable to jump to " + regionEncoding + " to recover a mate"); + } + + while (sam_itr_next(htsFilePtr_, htsRegionPtr_, htsAlignmentPtr_) >= 0) + { + const bool isSecondaryAlignment = htsAlignmentPtr_->core.flag & BAM_FSECONDARY; + const bool isSupplementaryAlignment = htsAlignmentPtr_->core.flag & BAM_FSUPPLEMENTARY; + const bool isPrimaryAlignment = !(isSecondaryAlignment || isSupplementaryAlignment); + if (!isPrimaryAlignment) + { + continue; + } + + Read putativeMate = htshelpers::decodeRead(htsAlignmentPtr_); + + const bool belongToSameFragment = read.fragmentId() == putativeMate.fragmentId(); + const bool formProperPair = read.mateNumber() != putativeMate.mateNumber(); + + if (belongToSameFragment && formProperPair) + { + mateStats = decodeAlignmentStats(htsAlignmentPtr_); + hts_itr_destroy(htsRegionPtr_); + return putativeMate; + } + } + hts_itr_destroy(htsRegionPtr_); + + return optional(); +} + +} + +} diff --git a/sample_analysis/MateExtractor.hh b/ehunter/sample/MateExtractor.hh similarity index 54% rename from sample_analysis/MateExtractor.hh rename to ehunter/sample/MateExtractor.hh index 8904c3f..949affb 100644 --- a/sample_analysis/MateExtractor.hh +++ b/ehunter/sample/MateExtractor.hh @@ -32,37 +32,37 @@ extern "C" #include "htslib/sam.h" } -#include "common/ReferenceContigInfo.hh" -#include "reads/Read.hh" +#include "core/Read.hh" +#include "core/ReferenceContigInfo.hh" namespace ehunter { namespace htshelpers { - class MateExtractor - { - public: - MateExtractor(const std::string& htsFilePath, const std::string& htsReferencePath); - ~MateExtractor(); +class MateExtractor +{ +public: + MateExtractor(const std::string& htsFilePath, const std::string& htsReferencePath); + ~MateExtractor(); - boost::optional - extractMate(const Read& read, const LinearAlignmentStats& alignmentStats, LinearAlignmentStats& mateStats); + boost::optional + extractMate(const Read& read, const LinearAlignmentStats& alignmentStats, LinearAlignmentStats& mateStats); - private: - void openFile(); - void loadHeader(); - void loadIndex(); +private: + void openFile(); + void loadHeader(); + void loadIndex(); - std::string htsFilePath_; - std::string htsReferencePath_; - ReferenceContigInfo contigInfo_; + std::string htsFilePath_; + std::string htsReferencePath_; + ReferenceContigInfo contigInfo_; - htsFile* htsFilePtr_ = nullptr; - bam_hdr_t* htsHeaderPtr_ = nullptr; - hts_idx_t* htsIndexPtr_ = nullptr; - bam1_t* htsAlignmentPtr_ = nullptr; - }; + htsFile* htsFilePtr_ = nullptr; + bam_hdr_t* htsHeaderPtr_ = nullptr; + hts_idx_t* htsIndexPtr_ = nullptr; + bam1_t* htsAlignmentPtr_ = nullptr; +}; } diff --git a/genotyping/tests/AlignMatrixTest.cpp b/ehunter/tests/AlignMatrixTest.cpp similarity index 98% rename from genotyping/tests/AlignMatrixTest.cpp rename to ehunter/tests/AlignMatrixTest.cpp index c8ddf6e..46a9162 100644 --- a/genotyping/tests/AlignMatrixTest.cpp +++ b/ehunter/tests/AlignMatrixTest.cpp @@ -26,8 +26,8 @@ #include "graphalign/GraphAlignment.hh" #include "graphalign/GraphAlignmentOperations.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using graphtools::Graph; using graphtools::GraphAlignment; diff --git a/classification/tests/AlignmentClassifierTest.cpp b/ehunter/tests/AlignmentClassifierTest.cpp similarity index 99% rename from classification/tests/AlignmentClassifierTest.cpp rename to ehunter/tests/AlignmentClassifierTest.cpp index 8d86352..dc42ebb 100644 --- a/classification/tests/AlignmentClassifierTest.cpp +++ b/ehunter/tests/AlignmentClassifierTest.cpp @@ -19,7 +19,7 @@ // // -#include "classification/AlignmentClassifier.hh" +#include "alignment/AlignmentClassifier.hh" #include #include diff --git a/classification/tests/AlignmentSummaryTest.cpp b/ehunter/tests/AlignmentSummaryTest.cpp similarity index 99% rename from classification/tests/AlignmentSummaryTest.cpp rename to ehunter/tests/AlignmentSummaryTest.cpp index cdf4568..bc930c7 100644 --- a/classification/tests/AlignmentSummaryTest.cpp +++ b/ehunter/tests/AlignmentSummaryTest.cpp @@ -25,7 +25,7 @@ #include "gtest/gtest.h" -#include "reads/Read.hh" +#include "core/Read.hh" using namespace ehunter; diff --git a/genotyping/tests/AlleleCheckerTest.cpp b/ehunter/tests/AlleleCheckerTest.cpp similarity index 98% rename from genotyping/tests/AlleleCheckerTest.cpp rename to ehunter/tests/AlleleCheckerTest.cpp index 3ba5d5a..5f9f6e9 100644 --- a/genotyping/tests/AlleleCheckerTest.cpp +++ b/ehunter/tests/AlleleCheckerTest.cpp @@ -23,7 +23,7 @@ #include "gtest/gtest.h" -#include "common/Common.hh" +#include "core/Common.hh" using namespace ehunter; @@ -65,7 +65,6 @@ TEST(AlleleChecker, LowCoverageCall) EXPECT_EQ(AlleleStatus::kUncertain, checker.check(5.0, 0, 15).status); EXPECT_EQ(AlleleStatus::kUncertain, checker.check(5.0, 1, 5).status); EXPECT_EQ(AlleleStatus::kPresent, checker.check(5.0, 7, 5).status); - } TEST(AllelePresenceChecker, HighCoverage) diff --git a/classification/tests/ClassifierOfAlignmentsToVariantTest.cpp b/ehunter/tests/ClassifierOfAlignmentsToVariantTest.cpp similarity index 95% rename from classification/tests/ClassifierOfAlignmentsToVariantTest.cpp rename to ehunter/tests/ClassifierOfAlignmentsToVariantTest.cpp index 249b478..4ae5355 100644 --- a/classification/tests/ClassifierOfAlignmentsToVariantTest.cpp +++ b/ehunter/tests/ClassifierOfAlignmentsToVariantTest.cpp @@ -19,7 +19,7 @@ // // -#include "classification/ClassifierOfAlignmentsToVariant.hh" +#include "alignment//ClassifierOfAlignmentsToVariant.hh" #include @@ -28,9 +28,9 @@ #include "graphalign/GraphAlignmentOperations.hh" #include "graphcore/Graph.hh" -#include "common/CountTable.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "core/CountTable.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using graphtools::Graph; using graphtools::GraphAlignment; diff --git a/ehunter/tests/ConcurrentQueueTest.cpp b/ehunter/tests/ConcurrentQueueTest.cpp new file mode 100644 index 0000000..d5a56ae --- /dev/null +++ b/ehunter/tests/ConcurrentQueueTest.cpp @@ -0,0 +1,41 @@ +// +// Expansion Hunter +// Copyright 2016-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "core/ConcurrentQueue.hh" + +#include "gtest/gtest.h" + +using namespace ehunter; + +TEST(ConcurrentQueueTest, SerialOps) +{ + // Sanity-check non-concurrent operation + ConcurrentQueue cq; + cq.push(1); + cq.push(2); + int r; + cq.pop(r); + EXPECT_FALSE(cq.empty()); + EXPECT_EQ(r, 1); + cq.pop(r); + EXPECT_TRUE(cq.empty()); + EXPECT_EQ(r, 2); +} diff --git a/common/tests/CountTableTest.cpp b/ehunter/tests/CountTableTest.cpp similarity index 98% rename from common/tests/CountTableTest.cpp rename to ehunter/tests/CountTableTest.cpp index af5cdfe..4db9a3d 100644 --- a/common/tests/CountTableTest.cpp +++ b/ehunter/tests/CountTableTest.cpp @@ -20,7 +20,7 @@ // // -#include "common/CountTable.hh" +#include "core/CountTable.hh" #include "gtest/gtest.h" diff --git a/genotyping/tests/FragLogliksTest.cpp b/ehunter/tests/FragLogliksTest.cpp similarity index 97% rename from genotyping/tests/FragLogliksTest.cpp rename to ehunter/tests/FragLogliksTest.cpp index 822265e..64a9f63 100644 --- a/genotyping/tests/FragLogliksTest.cpp +++ b/ehunter/tests/FragLogliksTest.cpp @@ -28,8 +28,8 @@ #include "genotyping/AlignMatrix.hh" #include "genotyping/RepeatGenotype.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using namespace ehunter; using namespace strgt; diff --git a/sample_analysis/tests/GenomeMaskTest.cpp b/ehunter/tests/GenomeMaskTest.cpp similarity index 97% rename from sample_analysis/tests/GenomeMaskTest.cpp rename to ehunter/tests/GenomeMaskTest.cpp index 284bb2b..52aea5d 100644 --- a/sample_analysis/tests/GenomeMaskTest.cpp +++ b/ehunter/tests/GenomeMaskTest.cpp @@ -19,7 +19,7 @@ // // -#include "sample_analysis/GenomeMask.hh" +#include "sample/GenomeMask.hh" #include "gtest/gtest.h" diff --git a/common/tests/GenomicRegionTest.cpp b/ehunter/tests/GenomicRegionTest.cpp similarity index 98% rename from common/tests/GenomicRegionTest.cpp rename to ehunter/tests/GenomicRegionTest.cpp index c11d1c5..54e3021 100644 --- a/common/tests/GenomicRegionTest.cpp +++ b/ehunter/tests/GenomicRegionTest.cpp @@ -20,7 +20,7 @@ // // -#include "common/GenomicRegion.hh" +#include "core/GenomicRegion.hh" #include diff --git a/alignment/tests/GraphAlignmentOperationsTest.cpp b/ehunter/tests/GraphAlignmentOperationsTest.cpp similarity index 98% rename from alignment/tests/GraphAlignmentOperationsTest.cpp rename to ehunter/tests/GraphAlignmentOperationsTest.cpp index 651ba98..5479fd0 100644 --- a/alignment/tests/GraphAlignmentOperationsTest.cpp +++ b/ehunter/tests/GraphAlignmentOperationsTest.cpp @@ -26,8 +26,8 @@ #include "graphalign/GraphAlignmentOperations.hh" #include "graphcore/Graph.hh" #include "graphcore/GraphBuilders.hh" -#include "input/RegionGraph.hh" -#include "region_spec/LocusSpecification.hh" +#include "io/RegionGraph.hh" +#include "locus/LocusSpecification.hh" using graphtools::decodeGraphAlignment; using graphtools::Graph; diff --git a/input/tests/GraphBlueprintTest.cpp b/ehunter/tests/GraphBlueprintTest.cpp similarity index 98% rename from input/tests/GraphBlueprintTest.cpp rename to ehunter/tests/GraphBlueprintTest.cpp index ffdf480..5ffb649 100644 --- a/input/tests/GraphBlueprintTest.cpp +++ b/ehunter/tests/GraphBlueprintTest.cpp @@ -19,7 +19,7 @@ // // -#include "input/GraphBlueprint.hh" +#include "io/GraphBlueprint.hh" #include "gtest/gtest.h" diff --git a/alignment/tests/GreedyAlignmentIntersectorTest.cpp b/ehunter/tests/GreedyAlignmentIntersectorTest.cpp similarity index 98% rename from alignment/tests/GreedyAlignmentIntersectorTest.cpp rename to ehunter/tests/GreedyAlignmentIntersectorTest.cpp index 7fc624f..a7ef86b 100644 --- a/alignment/tests/GreedyAlignmentIntersectorTest.cpp +++ b/ehunter/tests/GreedyAlignmentIntersectorTest.cpp @@ -27,8 +27,8 @@ #include "graphcore/Graph.hh" #include "graphcore/Path.hh" -#include "input/RegionGraph.hh" -#include "region_spec/LocusSpecification.hh" +#include "io/RegionGraph.hh" +#include "locus/LocusSpecification.hh" using graphtools::Graph; using graphtools::Path; diff --git a/alignment/tests/HighQualityBaseRunFinderTest.cpp b/ehunter/tests/HighQualityBaseRunFinderTest.cpp similarity index 100% rename from alignment/tests/HighQualityBaseRunFinderTest.cpp rename to ehunter/tests/HighQualityBaseRunFinderTest.cpp diff --git a/stats/tests/LocusStatsTest.cpp b/ehunter/tests/LocusStatsTest.cpp similarity index 98% rename from stats/tests/LocusStatsTest.cpp rename to ehunter/tests/LocusStatsTest.cpp index b440e98..ea012eb 100644 --- a/stats/tests/LocusStatsTest.cpp +++ b/ehunter/tests/LocusStatsTest.cpp @@ -19,7 +19,7 @@ // // -#include "stats/LocusStats.hh" +#include "core/LocusStats.hh" #include "gtest/gtest.h" diff --git a/stats/tests/ReadSupportCalculatorTest.cpp b/ehunter/tests/ReadSupportCalculatorTest.cpp similarity index 96% rename from stats/tests/ReadSupportCalculatorTest.cpp rename to ehunter/tests/ReadSupportCalculatorTest.cpp index 7d84763..f689b07 100644 --- a/stats/tests/ReadSupportCalculatorTest.cpp +++ b/ehunter/tests/ReadSupportCalculatorTest.cpp @@ -20,11 +20,11 @@ // // -#include "stats/ReadSupportCalculator.hh" +#include "core/ReadSupportCalculator.hh" #include "gtest/gtest.h" -#include "common/Common.hh" +#include "core/Common.hh" using namespace ehunter; diff --git a/reads/tests/ReadTest.cpp b/ehunter/tests/ReadTest.cpp similarity index 98% rename from reads/tests/ReadTest.cpp rename to ehunter/tests/ReadTest.cpp index afafefe..9daf849 100644 --- a/reads/tests/ReadTest.cpp +++ b/ehunter/tests/ReadTest.cpp @@ -19,7 +19,7 @@ // // -#include "reads/Read.hh" +#include "core/Read.hh" #include "gtest/gtest.h" diff --git a/input/tests/RegionGraphTest.cpp b/ehunter/tests/RegionGraphTest.cpp similarity index 94% rename from input/tests/RegionGraphTest.cpp rename to ehunter/tests/RegionGraphTest.cpp index 72e5ddb..a1e16e2 100644 --- a/input/tests/RegionGraphTest.cpp +++ b/ehunter/tests/RegionGraphTest.cpp @@ -19,14 +19,14 @@ // // -#include "input/RegionGraph.hh" +#include "io/RegionGraph.hh" #include "gtest/gtest.h" #include "graphcore/Graph.hh" -#include "input/GraphBlueprint.hh" -#include "region_spec/LocusSpecification.hh" +#include "io/GraphBlueprint.hh" +#include "locus/LocusSpecification.hh" using graphtools::Graph; using std::string; diff --git a/region_analysis/tests/RepeatAnalyzerTest.cpp b/ehunter/tests/RepeatAnalyzerTest.cpp similarity index 95% rename from region_analysis/tests/RepeatAnalyzerTest.cpp rename to ehunter/tests/RepeatAnalyzerTest.cpp index 8d04e35..f2701cb 100644 --- a/region_analysis/tests/RepeatAnalyzerTest.cpp +++ b/ehunter/tests/RepeatAnalyzerTest.cpp @@ -19,15 +19,15 @@ // // -#include "region_analysis/RepeatAnalyzer.hh" +#include "locus/RepeatAnalyzer.hh" #include "gtest/gtest.h" #include "graphalign/GraphAlignment.hh" #include "graphalign/GraphAlignmentOperations.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using graphtools::Graph; using graphtools::GraphAlignment; diff --git a/genotyping/tests/RepeatGenotypeTest.cpp b/ehunter/tests/RepeatGenotypeTest.cpp similarity index 99% rename from genotyping/tests/RepeatGenotypeTest.cpp rename to ehunter/tests/RepeatGenotypeTest.cpp index 21702e4..5749bec 100644 --- a/genotyping/tests/RepeatGenotypeTest.cpp +++ b/ehunter/tests/RepeatGenotypeTest.cpp @@ -24,7 +24,7 @@ #include "gtest/gtest.h" -#include "common/Common.hh" +#include "core/Common.hh" using std::vector; diff --git a/genotyping/tests/SmallVariantGenotyperTest.cpp b/ehunter/tests/SmallVariantGenotyperTest.cpp similarity index 98% rename from genotyping/tests/SmallVariantGenotyperTest.cpp rename to ehunter/tests/SmallVariantGenotyperTest.cpp index dce4484..3eac3a5 100644 --- a/genotyping/tests/SmallVariantGenotyperTest.cpp +++ b/ehunter/tests/SmallVariantGenotyperTest.cpp @@ -25,7 +25,7 @@ #include "gtest/gtest.h" -#include "common/Common.hh" +#include "core/Common.hh" using namespace ehunter; diff --git a/alignment/tests/SoftclippingAlignerTest.cpp b/ehunter/tests/SoftclippingAlignerTest.cpp similarity index 96% rename from alignment/tests/SoftclippingAlignerTest.cpp rename to ehunter/tests/SoftclippingAlignerTest.cpp index 145eef5..889583d 100644 --- a/alignment/tests/SoftclippingAlignerTest.cpp +++ b/ehunter/tests/SoftclippingAlignerTest.cpp @@ -30,9 +30,9 @@ #include "graphalign/GraphAlignmentOperations.hh" #include "graphcore/Graph.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" -#include "region_spec/LocusSpecification.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" +#include "locus/LocusSpecification.hh" using graphtools::decodeGraphAlignment; using graphtools::GappedGraphAligner; diff --git a/genotyping/tests/StrAlignTest.cpp b/ehunter/tests/StrAlignTest.cpp similarity index 99% rename from genotyping/tests/StrAlignTest.cpp rename to ehunter/tests/StrAlignTest.cpp index b525acd..cfd9e86 100644 --- a/genotyping/tests/StrAlignTest.cpp +++ b/ehunter/tests/StrAlignTest.cpp @@ -26,8 +26,8 @@ #include "graphalign/GraphAlignment.hh" #include "graphalign/GraphAlignmentOperations.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using graphtools::Graph; using graphtools::GraphAlignment; diff --git a/genotyping/tests/StrGenotyperTest.cpp b/ehunter/tests/StrGenotyperTest.cpp similarity index 98% rename from genotyping/tests/StrGenotyperTest.cpp rename to ehunter/tests/StrGenotyperTest.cpp index 39389c1..a121e79 100644 --- a/genotyping/tests/StrGenotyperTest.cpp +++ b/ehunter/tests/StrGenotyperTest.cpp @@ -30,8 +30,8 @@ #include "genotyping/AlignMatrix.hh" #include "genotyping/RepeatGenotype.hh" -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" +#include "io/GraphBlueprint.hh" +#include "io/RegionGraph.hh" using namespace ehunter; using namespace strgt; diff --git a/ehunter/tests/UnitTests.cpp b/ehunter/tests/UnitTests.cpp new file mode 100644 index 0000000..15c849e --- /dev/null +++ b/ehunter/tests/UnitTests.cpp @@ -0,0 +1,28 @@ +// +// ExpansionHunter +// Copyright 2016-2021 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "gtest/gtest.h" + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/stats/tests/WeightedPurityCalculatorTest.cpp b/ehunter/tests/WeightedPurityCalculatorTest.cpp similarity index 96% rename from stats/tests/WeightedPurityCalculatorTest.cpp rename to ehunter/tests/WeightedPurityCalculatorTest.cpp index 8c2735e..31f1ff2 100644 --- a/stats/tests/WeightedPurityCalculatorTest.cpp +++ b/ehunter/tests/WeightedPurityCalculatorTest.cpp @@ -19,7 +19,7 @@ // // -#include "stats/WeightedPurityCalculator.hh" +#include "core/WeightedPurityCalculator.hh" #include diff --git a/ehunter/thirdparty/ctpl/README.md b/ehunter/thirdparty/ctpl/README.md new file mode 100644 index 0000000..06b4567 --- /dev/null +++ b/ehunter/thirdparty/ctpl/README.md @@ -0,0 +1,5 @@ + +CTPL thread pool library - https://github.com/vit-vit/CTPL + +This is a header-only library so no additional build integration is required. + diff --git a/ehunter/thirdparty/ctpl/ctpl-0.0.2/LICENSE b/ehunter/thirdparty/ctpl/ctpl-0.0.2/LICENSE new file mode 100644 index 0000000..0cf416b --- /dev/null +++ b/ehunter/thirdparty/ctpl/ctpl-0.0.2/LICENSE @@ -0,0 +1,191 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + + Copyright (C) 2014 by Vitaliy Vitsentiy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ehunter/thirdparty/ctpl/ctpl-0.0.2/README.md b/ehunter/thirdparty/ctpl/ctpl-0.0.2/README.md new file mode 100644 index 0000000..9f0c2b6 --- /dev/null +++ b/ehunter/thirdparty/ctpl/ctpl-0.0.2/README.md @@ -0,0 +1,59 @@ +CTPL +==== + +Modern and efficient C++ Thread Pool Library + + +A thread pool is a programming pattern for parallel execution of jobs, http://en.wikipedia.org/wiki/Thread_pool_pattern. + +More specifically, there are some threads dedicated to the pool and a container of jobs. The jobs come to the pool dynamically. A job is fetched and deleted from the container when there is an idle thread. The job is then run on that thread. + +A thread pool is helpful when you want to minimize time of loading and destroying threads and when you want to limit the number of parallel jobs that run simultanuasly. For example, time consuming event handlers may be processed in a thread pool to make UI more responsive. + +Features: +- standard c++ language, tested to compile on MS Visual Studio 2013 (2012?), gcc 4.8.2 and mingw 4.8.1(with posix threads) +- simple but effiecient solution, one header only, no need to compile a binary library +- query the number of idle threads and resize the pool dynamically +- one API to push to the thread pool any collable object: lambdas, functors, functions, result of bind expression +- collable objects with variadic number of parameters plus index of the thread running the object +- automatic template argument deduction +- get returned value of any type with standard c++ futures +- get fired exceptions with standard c++ futures +- use for any purpose under Apache license +- two variants, one depends on Boost Lockfree Queue library, http://boost.org, which is a header only library + + +Sample usage + +void first(int id) { + std::cout << "hello from " << id << '\n'; +} + + struct Second { + void operator()(int id) const { + std::cout << "hello from " << id << '\n'; + } +} second; + +void third(int id, const std::string & additional_param) {} + + +int main () { + + ctpl::thread_pool p(2 /* two threads in the pool */); + + p.push(first); // function + + p.push(third, "additional_param"); + + p.push( [] (int id){ + std::cout << "hello from " << id << '\n'; +}); // lambda + + p.push(std::ref(second)); // functor, reference + + p.push(const_cast<const Second &>(second)); // functor, copy ctor + + p.push(std::move(second)); // functor, move ctor + +} diff --git a/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl.h b/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl.h new file mode 100644 index 0000000..64f650d --- /dev/null +++ b/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl.h @@ -0,0 +1,240 @@ + +/********************************************************* + * + * Copyright (C) 2014 by Vitaliy Vitsentiy + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *********************************************************/ + + +#ifndef __ctpl_thread_pool_H__ +#define __ctpl_thread_pool_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef _ctplThreadPoolLength_ +#define _ctplThreadPoolLength_ 100 +#endif + + +// thread pool to run user's functors with signature +// ret func(int id, other_params) +// where id is the index of the thread that runs the functor +// ret is some return type + + +namespace ctpl { + + class thread_pool { + + public: + + thread_pool() : q(_ctplThreadPoolLength_) { this->init(); } + thread_pool(int nThreads, int queueSize = _ctplThreadPoolLength_) : q(queueSize) { this->init(); this->resize(nThreads); } + + // the destructor waits for all the functions in the queue to be finished + ~thread_pool() { + this->stop(true); + } + + // get the number of running threads in the pool + int size() { return static_cast(this->threads.size()); } + + // number of idle threads + int n_idle() { return this->nWaiting; } + std::thread & get_thread(int i) { return *this->threads[i]; } + + // change the number of threads in the pool + // should be called from one thread, otherwise be careful to not interleave, also with this->stop() + // nThreads must be >= 0 + void resize(int nThreads) { + if (!this->isStop && !this->isDone) { + int oldNThreads = static_cast(this->threads.size()); + if (oldNThreads <= nThreads) { // if the number of threads is increased + this->threads.resize(nThreads); + this->flags.resize(nThreads); + + for (int i = oldNThreads; i < nThreads; ++i) { + this->flags[i] = std::make_shared>(false); + this->set_thread(i); + } + } + else { // the number of threads is decreased + for (int i = oldNThreads - 1; i >= nThreads; --i) { + *this->flags[i] = true; // this thread will finish + this->threads[i]->detach(); + } + { + // stop the detached threads that were waiting + std::unique_lock lock(this->mutex); + this->cv.notify_all(); + } + this->threads.resize(nThreads); // safe to delete because the threads are detached + this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals + } + } + } + + // empty the queue + void clear_queue() { + std::function * _f; + while (this->q.pop(_f)) + delete _f; // empty the queue + } + + // pops a functional wraper to the original function + std::function pop() { + std::function * _f = nullptr; + this->q.pop(_f); + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + + std::function f; + if (_f) + f = *_f; + return f; + } + + + // wait for all computing threads to finish and stop all threads + // may be called asyncronously to not pause the calling thread while waiting + // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions + void stop(bool isWait = false) { + if (!isWait) { + if (this->isStop) + return; + this->isStop = true; + for (int i = 0, n = this->size(); i < n; ++i) { + *this->flags[i] = true; // command the threads to stop + } + this->clear_queue(); // empty the queue + } + else { + if (this->isDone || this->isStop) + return; + this->isDone = true; // give the waiting threads a command to finish + } + { + std::unique_lock lock(this->mutex); + this->cv.notify_all(); // stop all waiting threads + } + for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish + if (this->threads[i]->joinable()) + this->threads[i]->join(); + } + // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads + // therefore delete them here + this->clear_queue(); + this->threads.clear(); + this->flags.clear(); + } + + template + auto push(F && f, Rest&&... rest) ->std::future { + auto pck = std::make_shared>( + std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) + ); + + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + + return pck->get_future(); + } + + // run the user's function that excepts argument int - id of the running thread. returned value is templatized + // operator returns std::future, where the user can get the result and rethrow the catched exceptins + template + auto push(F && f) ->std::future { + auto pck = std::make_shared>(std::forward(f)); + + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + + return pck->get_future(); + } + + + private: + + // deleted + thread_pool(const thread_pool &);// = delete; + thread_pool(thread_pool &&);// = delete; + thread_pool & operator=(const thread_pool &);// = delete; + thread_pool & operator=(thread_pool &&);// = delete; + + void set_thread(int i) { + std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag + auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { + std::atomic & _flag = *flag; + std::function * _f; + bool isPop = this->q.pop(_f); + while (true) { + while (isPop) { // if there is anything in the queue + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + (*_f)(i); + + if (_flag) + return; // the thread is wanted to stop, return even if the queue is not empty yet + else + isPop = this->q.pop(_f); + } + + // the queue is empty here, wait for the next command + std::unique_lock lock(this->mutex); + ++this->nWaiting; + this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); + --this->nWaiting; + + if (!isPop) + return; // if the queue is empty and this->isDone == true or *flag then return + } + }; + this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() + } + + void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } + + std::vector> threads; + std::vector>> flags; + mutable boost::lockfree::queue *> q; + std::atomic isDone; + std::atomic isStop; + std::atomic nWaiting; // how many threads are waiting + + std::mutex mutex; + std::condition_variable cv; + }; + +} + +#endif // __ctpl_thread_pool_H__ + diff --git a/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl_stl.h b/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl_stl.h new file mode 100644 index 0000000..5956cf0 --- /dev/null +++ b/ehunter/thirdparty/ctpl/ctpl-0.0.2/ctpl_stl.h @@ -0,0 +1,251 @@ +/********************************************************* +* +* Copyright (C) 2014 by Vitaliy Vitsentiy +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*********************************************************/ + + +#ifndef __ctpl_stl_thread_pool_H__ +#define __ctpl_stl_thread_pool_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// thread pool to run user's functors with signature +// ret func(int id, other_params) +// where id is the index of the thread that runs the functor +// ret is some return type + + +namespace ctpl { + + namespace detail { + template + class Queue { + public: + bool push(T const & value) { + std::unique_lock lock(this->mutex); + this->q.push(value); + return true; + } + // deletes the retrieved element, do not use for non integral types + bool pop(T & v) { + std::unique_lock lock(this->mutex); + if (this->q.empty()) + return false; + v = this->q.front(); + this->q.pop(); + return true; + } + bool empty() { + std::unique_lock lock(this->mutex); + return this->q.empty(); + } + private: + std::queue q; + std::mutex mutex; + }; + } + + class thread_pool { + + public: + + thread_pool() { this->init(); } + thread_pool(int nThreads) { this->init(); this->resize(nThreads); } + + // the destructor waits for all the functions in the queue to be finished + ~thread_pool() { + this->stop(true); + } + + // get the number of running threads in the pool + int size() { return static_cast(this->threads.size()); } + + // number of idle threads + int n_idle() { return this->nWaiting; } + std::thread & get_thread(int i) { return *this->threads[i]; } + + // change the number of threads in the pool + // should be called from one thread, otherwise be careful to not interleave, also with this->stop() + // nThreads must be >= 0 + void resize(int nThreads) { + if (!this->isStop && !this->isDone) { + int oldNThreads = static_cast(this->threads.size()); + if (oldNThreads <= nThreads) { // if the number of threads is increased + this->threads.resize(nThreads); + this->flags.resize(nThreads); + + for (int i = oldNThreads; i < nThreads; ++i) { + this->flags[i] = std::make_shared>(false); + this->set_thread(i); + } + } + else { // the number of threads is decreased + for (int i = oldNThreads - 1; i >= nThreads; --i) { + *this->flags[i] = true; // this thread will finish + this->threads[i]->detach(); + } + { + // stop the detached threads that were waiting + std::unique_lock lock(this->mutex); + this->cv.notify_all(); + } + this->threads.resize(nThreads); // safe to delete because the threads are detached + this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals + } + } + } + + // empty the queue + void clear_queue() { + std::function * _f; + while (this->q.pop(_f)) + delete _f; // empty the queue + } + + // pops a functional wrapper to the original function + std::function pop() { + std::function * _f = nullptr; + this->q.pop(_f); + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + std::function f; + if (_f) + f = *_f; + return f; + } + + // wait for all computing threads to finish and stop all threads + // may be called asynchronously to not pause the calling thread while waiting + // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions + void stop(bool isWait = false) { + if (!isWait) { + if (this->isStop) + return; + this->isStop = true; + for (int i = 0, n = this->size(); i < n; ++i) { + *this->flags[i] = true; // command the threads to stop + } + this->clear_queue(); // empty the queue + } + else { + if (this->isDone || this->isStop) + return; + this->isDone = true; // give the waiting threads a command to finish + } + { + std::unique_lock lock(this->mutex); + this->cv.notify_all(); // stop all waiting threads + } + for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish + if (this->threads[i]->joinable()) + this->threads[i]->join(); + } + // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads + // therefore delete them here + this->clear_queue(); + this->threads.clear(); + this->flags.clear(); + } + + template + auto push(F && f, Rest&&... rest) ->std::future { + auto pck = std::make_shared>( + std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) + ); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + // run the user's function that excepts argument int - id of the running thread. returned value is templatized + // operator returns std::future, where the user can get the result and rethrow the catched exceptins + template + auto push(F && f) ->std::future { + auto pck = std::make_shared>(std::forward(f)); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + + private: + + // deleted + thread_pool(const thread_pool &);// = delete; + thread_pool(thread_pool &&);// = delete; + thread_pool & operator=(const thread_pool &);// = delete; + thread_pool & operator=(thread_pool &&);// = delete; + + void set_thread(int i) { + std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag + auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { + std::atomic & _flag = *flag; + std::function * _f; + bool isPop = this->q.pop(_f); + while (true) { + while (isPop) { // if there is anything in the queue + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + (*_f)(i); + if (_flag) + return; // the thread is wanted to stop, return even if the queue is not empty yet + else + isPop = this->q.pop(_f); + } + // the queue is empty here, wait for the next command + std::unique_lock lock(this->mutex); + ++this->nWaiting; + this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); + --this->nWaiting; + if (!isPop) + return; // if the queue is empty and this->isDone == true or *flag then return + } + }; + this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() + } + + void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } + + std::vector> threads; + std::vector>> flags; + detail::Queue *> q; + std::atomic isDone; + std::atomic isStop; + std::atomic nWaiting; // how many threads are waiting + + std::mutex mutex; + std::condition_variable cv; + }; + +} + +#endif // __ctpl_stl_thread_pool_H__ diff --git a/thirdparty/graph-tools-master/CMakeLists.txt b/ehunter/thirdparty/graph-tools-master-f421f4c/CMakeLists.txt old mode 100755 new mode 100644 similarity index 76% rename from thirdparty/graph-tools-master/CMakeLists.txt rename to ehunter/thirdparty/graph-tools-master-f421f4c/CMakeLists.txt index ff43a2f..f45b519 --- a/thirdparty/graph-tools-master/CMakeLists.txt +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/CMakeLists.txt @@ -17,18 +17,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") if (CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64$") - if(NOT GRAPHTOOLS_AVX2) - set(GRAPHTOOLS_VECTORIZATION "-msse4.2") - else(NOT GRAPHTOOLS_AVX2) - set(GRAPHTOOLS_VECTORIZATION "-mavx2") - endif(NOT GRAPHTOOLS_AVX2) + if (NOT GRAPHTOOLS_AVX2) + set(GRAPHTOOLS_VECTORIZATION "-msse4.2") + else (NOT GRAPHTOOLS_AVX2) + set(GRAPHTOOLS_VECTORIZATION "-mavx2") + endif (NOT GRAPHTOOLS_AVX2) endif (CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64$") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set (GRAPHTOOLS_CXX_OPTIMIZATION_FLAGS "${GRAPHTOOLS_VECTORIZATION} -O2 -ftree-vectorize -finline-functions -fpredictive-commoning -fgcse-after-reload -funswitch-loops -ftree-slp-vectorize -fvect-cost-model -fipa-cp-clone -ftree-phiprop") -endif() - -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GRAPHTOOLS_CXX_OPTIMIZATION_FLAGS} -Wall -Werror -pedantic -Wsign-compare -Wno-missing-braces") + set(GRAPHTOOLS_CXX_OPTIMIZATION_FLAGS "${GRAPHTOOLS_VECTORIZATION} -O2 -ftree-vectorize -finline-functions -fpredictive-commoning -fgcse-after-reload -funswitch-loops -ftree-slp-vectorize -fvect-cost-model -fipa-cp-clone -ftree-phiprop") +endif () + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GRAPHTOOLS_CXX_OPTIMIZATION_FLAGS} -Wall -Werror -pedantic -Wsign-compare -Wno-missing-braces -Wno-deprecated-copy -Wno-unknown-warning-option") if (USE_ASAN) SET(CLANG_ASAN "-O1 -g -fsanitize=address -fno-omit-frame-pointer") @@ -51,6 +51,7 @@ file(GLOB SOURCES "src/graphalign/*.cpp" "src/graphalign/*/*.cpp" "src/graphcore add_library(graphtools ${SOURCES}) target_include_directories(graphtools PUBLIC "include") target_include_directories(graphtools PUBLIC "external/include") +target_include_directories(graphtools PUBLIC "external/sparsepp/sparsepp-e40d7a0") target_link_libraries(graphtools Boost::boost) target_compile_features(graphtools PRIVATE cxx_range_for) @@ -59,4 +60,4 @@ if (BUILD_TESTS) # Download and unpack googletest at configure time include(GetGoogleTest) add_subdirectory(tests) -endif (BUILD_TESTS) \ No newline at end of file +endif (BUILD_TESTS) diff --git a/thirdparty/graph-tools-master/Dockerfile b/ehunter/thirdparty/graph-tools-master-f421f4c/Dockerfile old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/Dockerfile rename to ehunter/thirdparty/graph-tools-master-f421f4c/Dockerfile diff --git a/thirdparty/graph-tools-master/LICENSE.txt b/ehunter/thirdparty/graph-tools-master-f421f4c/LICENSE.txt old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/LICENSE.txt rename to ehunter/thirdparty/graph-tools-master-f421f4c/LICENSE.txt diff --git a/thirdparty/graph-tools-master/README.md b/ehunter/thirdparty/graph-tools-master-f421f4c/README.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/README.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/README.md diff --git a/thirdparty/graph-tools-master/RELEASES.md b/ehunter/thirdparty/graph-tools-master-f421f4c/RELEASES.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/RELEASES.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/RELEASES.md diff --git a/thirdparty/graph-tools-master/cmake/GetGoogleTest.cmake b/ehunter/thirdparty/graph-tools-master-f421f4c/cmake/GetGoogleTest.cmake old mode 100755 new mode 100644 similarity index 93% rename from thirdparty/graph-tools-master/cmake/GetGoogleTest.cmake rename to ehunter/thirdparty/graph-tools-master-f421f4c/cmake/GetGoogleTest.cmake index 2ce8a17..aded63a --- a/thirdparty/graph-tools-master/cmake/GetGoogleTest.cmake +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/cmake/GetGoogleTest.cmake @@ -2,14 +2,14 @@ # Download and unpack googletest at configure time FILE(WRITE "${CMAKE_BINARY_DIR}/external/googletest-build/CMakeLists.txt" "\ -cmake_minimum_required(VERSION 2.8.5) +cmake_minimum_required(VERSION 2.8.12) project(googletest-build NONE) include(ExternalProject) ExternalProject_Add(googletest # GIT_REPOSITORY https://github.com/google/googletest.git # GIT_TAG release-1.8.0 - URL \"${CMAKE_SOURCE_DIR}/external/googletest-release-1.8.0.tar.gz\" - URL_HASH MD5=16877098823401d1bf2ed7891d7dce36 + URL \"${CMAKE_SOURCE_DIR}/external/googletest-release-1.10.0.tar.gz\" + URL_HASH MD5=ecd1fa65e7de707cd5c00bdac56022cd SOURCE_DIR \"${CMAKE_BINARY_DIR}/external/googletest-src\" BINARY_DIR \"${CMAKE_BINARY_DIR}/external/googletest-build\" CONFIGURE_COMMAND \"\" diff --git a/thirdparty/graph-tools-master/docs/alignment.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/alignment.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/alignment.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/alignment.md diff --git a/thirdparty/graph-tools-master/docs/development.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/development.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/development.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/development.md diff --git a/thirdparty/graph-tools-master/docs/graph.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/graph.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/graph.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/graph.md diff --git a/thirdparty/graph-tools-master/docs/path_families.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/path_families.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/path_families.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/path_families.md diff --git a/thirdparty/graph-tools-master/docs/paths.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/paths.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/paths.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/paths.md diff --git a/thirdparty/graph-tools-master/docs/query_and_reference_sequences.md b/ehunter/thirdparty/graph-tools-master-f421f4c/docs/query_and_reference_sequences.md old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/docs/query_and_reference_sequences.md rename to ehunter/thirdparty/graph-tools-master-f421f4c/docs/query_and_reference_sequences.md diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/googletest-release-1.10.0.tar.gz b/ehunter/thirdparty/graph-tools-master-f421f4c/external/googletest-release-1.10.0.tar.gz new file mode 100644 index 0000000..ab10868 Binary files /dev/null and b/ehunter/thirdparty/graph-tools-master-f421f4c/external/googletest-release-1.10.0.tar.gz differ diff --git a/thirdparty/graph-tools-master/external/include/nlohmann/json.hpp b/ehunter/thirdparty/graph-tools-master-f421f4c/external/include/nlohmann/json.hpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/external/include/nlohmann/json.hpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/external/include/nlohmann/json.hpp diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/README.md b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/README.md new file mode 100644 index 0000000..27967a6 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/README.md @@ -0,0 +1,2 @@ +Contains a subset (LICENSE and headers) of the Sparsepp library (https://github.com/greg7mdp/sparsepp), commit e40d7a0. + diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/LICENSE b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/LICENSE new file mode 100644 index 0000000..865d273 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/LICENSE @@ -0,0 +1,36 @@ +// ---------------------------------------------------------------------- +// Copyright (c) 2016, Gregory Popovitch - greg7mdp@gmail.com +// All rights reserved. +// +// This work is derived from Google's sparsehash library +// +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// ---------------------------------------------------------------------- + diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp.h new file mode 100644 index 0000000..26aa200 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp.h @@ -0,0 +1,4361 @@ +#if !defined(sparsepp_h_guard_) +#define sparsepp_h_guard_ + + +// ---------------------------------------------------------------------- +// Copyright (c) 2016, Gregory Popovitch - greg7mdp@gmail.com +// All rights reserved. +// +// This work is derived from Google's sparsehash library +// +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// ---------------------------------------------------------------------- + + +// some macros for portability +// --------------------------- +// includes +// -------- +#include +#include +#include +#include // for numeric_limits +#include // For swap(), eg +#include // for iterator tags +#include // for equal_to<>, select1st<>, std::unary_function, etc +#include // for alloc, uninitialized_copy, uninitialized_fill +#include // for malloc/realloc/free +#include // for ptrdiff_t +#include // for placement new +#include // For length_error +#include // for pair<> +#include +#include +#include + +#include "spp_stdint.h" // includes spp_config.h +#include "spp_traits.h" +#include "spp_utils.h" + +#ifdef SPP_INCLUDE_SPP_ALLOC + #include "spp_dlalloc.h" +#endif + +#if !defined(SPP_NO_CXX11_HDR_INITIALIZER_LIST) + #include +#endif + +#if (SPP_GROUP_SIZE == 32) + #define SPP_SHIFT_ 5 + #define SPP_MASK_ 0x1F + typedef uint32_t group_bm_type; +#elif (SPP_GROUP_SIZE == 64) + #define SPP_SHIFT_ 6 + #define SPP_MASK_ 0x3F + typedef uint64_t group_bm_type; +#else + #error "SPP_GROUP_SIZE must be either 32 or 64" +#endif + +namespace spp_ { + +// ---------------------------------------------------------------------- +// U T I L F U N C T I O N S +// ---------------------------------------------------------------------- +template +inline void throw_exception(const E& exception) +{ +#if !defined(SPP_NO_EXCEPTIONS) + throw exception; +#else + assert(0); + abort(); +#endif +} + +// ---------------------------------------------------------------------- +// M U T A B L E P A I R H A C K +// turn std::pair into mutable std::pair +// ---------------------------------------------------------------------- +template +struct cvt +{ + typedef T type; +}; + +template +struct cvt > +{ + typedef std::pair type; +}; + +template +struct cvt > +{ + typedef const std::pair type; +}; + +// ---------------------------------------------------------------------- +// M O V E I T E R A T O R +// ---------------------------------------------------------------------- +#ifdef SPP_NO_CXX11_RVALUE_REFERENCES + #define MK_MOVE_IT(p) (p) +#else + #define MK_MOVE_IT(p) std::make_move_iterator(p) +#endif + + +// ---------------------------------------------------------------------- +// I N T E R N A L S T U F F +// ---------------------------------------------------------------------- +#ifdef SPP_NO_CXX11_STATIC_ASSERT + template struct SppCompileAssert { }; + #define SPP_COMPILE_ASSERT(expr, msg) \ + SPP_ATTRIBUTE_UNUSED typedef SppCompileAssert<(bool(expr))> spp_bogus_[bool(expr) ? 1 : -1] +#else + #define SPP_COMPILE_ASSERT static_assert +#endif + +namespace sparsehash_internal +{ + +// Adaptor methods for reading/writing data from an INPUT or OUPTUT +// variable passed to serialize() or unserialize(). For now we +// have implemented INPUT/OUTPUT for FILE*, istream*/ostream* (note +// they are pointers, unlike typical use), or else a pointer to +// something that supports a Read()/Write() method. +// +// For technical reasons, we implement read_data/write_data in two +// stages. The actual work is done in *_data_internal, which takes +// the stream argument twice: once as a template type, and once with +// normal type information. (We only use the second version.) We do +// this because of how C++ picks what function overload to use. If we +// implemented this the naive way: +// bool read_data(istream* is, const void* data, size_t length); +// template read_data(T* fp, const void* data, size_t length); +// C++ would prefer the second version for every stream type except +// istream. However, we want C++ to prefer the first version for +// streams that are *subclasses* of istream, such as istringstream. +// This is not possible given the way template types are resolved. So +// we split the stream argument in two, one of which is templated and +// one of which is not. The specialized functions (like the istream +// version above) ignore the template arg and use the second, 'type' +// arg, getting subclass matching as normal. The 'catch-all' +// functions (the second version above) use the template arg to deduce +// the type, and use a second, void* arg to achieve the desired +// 'catch-all' semantics. + + // ----- low-level I/O for FILE* ---- + + template + inline bool read_data_internal(Ignored* /*unused*/, FILE* fp, + void* data, size_t length) + { + return fread(data, length, 1, fp) == 1; + } + + template + inline bool write_data_internal(Ignored* /*unused*/, FILE* fp, + const void* data, size_t length) + { + return fwrite(data, length, 1, fp) == 1; + } + + // ----- low-level I/O for iostream ---- + + // We want the caller to be responsible for #including , not + // us, because iostream is a big header! According to the standard, + // it's only legal to delay the instantiation the way we want to if + // the istream/ostream is a template type. So we jump through hoops. + template + inline bool read_data_internal_for_istream(ISTREAM* fp, + void* data, size_t length) + { + return fp->read(reinterpret_cast(data), + static_cast(length)).good(); + } + template + inline bool read_data_internal(Ignored* /*unused*/, std::istream* fp, + void* data, size_t length) + { + return read_data_internal_for_istream(fp, data, length); + } + + template + inline bool write_data_internal_for_ostream(OSTREAM* fp, + const void* data, size_t length) + { + return fp->write(reinterpret_cast(data), + static_cast(length)).good(); + } + template + inline bool write_data_internal(Ignored* /*unused*/, std::ostream* fp, + const void* data, size_t length) + { + return write_data_internal_for_ostream(fp, data, length); + } + + // ----- low-level I/O for custom streams ---- + + // The INPUT type needs to support a Read() method that takes a + // buffer and a length and returns the number of bytes read. + template + inline bool read_data_internal(INPUT* fp, void* /*unused*/, + void* data, size_t length) + { + return static_cast(fp->Read(data, length)) == length; + } + + // The OUTPUT type needs to support a Write() operation that takes + // a buffer and a length and returns the number of bytes written. + template + inline bool write_data_internal(OUTPUT* fp, void* /*unused*/, + const void* data, size_t length) + { + return static_cast(fp->Write(data, length)) == length; + } + + // ----- low-level I/O: the public API ---- + + template + inline bool read_data(INPUT* fp, void* data, size_t length) + { + return read_data_internal(fp, fp, data, length); + } + + template + inline bool write_data(OUTPUT* fp, const void* data, size_t length) + { + return write_data_internal(fp, fp, data, length); + } + + // Uses read_data() and write_data() to read/write an integer. + // length is the number of bytes to read/write (which may differ + // from sizeof(IntType), allowing us to save on a 32-bit system + // and load on a 64-bit system). Excess bytes are taken to be 0. + // INPUT and OUTPUT must match legal inputs to read/write_data (above). + // -------------------------------------------------------------------- + template + bool read_bigendian_number(INPUT* fp, IntType* value, size_t length) + { + *value = 0; + unsigned char byte; + // We require IntType to be unsigned or else the shifting gets all screwy. + SPP_COMPILE_ASSERT(static_cast(-1) > static_cast(0), "serializing_int_requires_an_unsigned_type"); + for (size_t i = 0; i < length; ++i) + { + if (!read_data(fp, &byte, sizeof(byte))) + return false; + *value |= static_cast(byte) << ((length - 1 - i) * 8); + } + return true; + } + + template + bool write_bigendian_number(OUTPUT* fp, IntType value, size_t length) + { + unsigned char byte; + // We require IntType to be unsigned or else the shifting gets all screwy. + SPP_COMPILE_ASSERT(static_cast(-1) > static_cast(0), "serializing_int_requires_an_unsigned_type"); + for (size_t i = 0; i < length; ++i) + { + byte = (sizeof(value) <= length-1 - i) + ? static_cast(0) : static_cast((value >> ((length-1 - i) * 8)) & 255); + if (!write_data(fp, &byte, sizeof(byte))) return false; + } + return true; + } + + // If your keys and values are simple enough, you can pass this + // serializer to serialize()/unserialize(). "Simple enough" means + // value_type is a POD type that contains no pointers. Note, + // however, we don't try to normalize endianness. + // This is the type used for NopointerSerializer. + // --------------------------------------------------------------- + template struct pod_serializer + { + template + bool operator()(INPUT* fp, value_type* value) const + { + return read_data(fp, value, sizeof(*value)); + } + + template + bool operator()(OUTPUT* fp, const value_type& value) const + { + return write_data(fp, &value, sizeof(value)); + } + }; + + + // Settings contains parameters for growing and shrinking the table. + // It also packages zero-size functor (ie. hasher). + // + // It does some munging of the hash value for the cases where + // the original hash function is not be very good. + // --------------------------------------------------------------- + template + class sh_hashtable_settings : public HashFunc + { + private: +#ifndef SPP_MIX_HASH + template struct Mixer + { + inline T operator()(T h) const { return h; } + }; +#else + template struct Mixer + { + inline T operator()(T h) const; + }; + + template struct Mixer + { + inline T operator()(T h) const + { + // from Thomas Wang - https://gist.github.com/badboy/6267743 + // --------------------------------------------------------- + h = (h ^ 61) ^ (h >> 16); + h = h + (h << 3); + h = h ^ (h >> 4); + h = h * 0x27d4eb2d; + h = h ^ (h >> 15); + return h; + } + }; + + template struct Mixer + { + inline T operator()(T h) const + { + // from Thomas Wang - https://gist.github.com/badboy/6267743 + // --------------------------------------------------------- + h = (~h) + (h << 21); // h = (h << 21) - h - 1; + h = h ^ (h >> 24); + h = (h + (h << 3)) + (h << 8); // h * 265 + h = h ^ (h >> 14); + h = (h + (h << 2)) + (h << 4); // h * 21 + h = h ^ (h >> 28); + h = h + (h << 31); + return h; + } + }; +#endif + + public: + typedef Key key_type; + typedef HashFunc hasher; + typedef SizeType size_type; + + public: + sh_hashtable_settings(const hasher& hf, + const float ht_occupancy_flt, + const float ht_empty_flt) + : hasher(hf), + enlarge_threshold_(0), + shrink_threshold_(0), + consider_shrink_(false), + num_ht_copies_(0) + { + set_enlarge_factor(ht_occupancy_flt); + set_shrink_factor(ht_empty_flt); + } + + size_t hash(const key_type& v) const + { + size_t h = hasher::operator()(v); + Mixer mixer; + + return mixer(h); + } + + float enlarge_factor() const { return enlarge_factor_; } + void set_enlarge_factor(float f) { enlarge_factor_ = f; } + float shrink_factor() const { return shrink_factor_; } + void set_shrink_factor(float f) { shrink_factor_ = f; } + + size_type enlarge_threshold() const { return enlarge_threshold_; } + void set_enlarge_threshold(size_type t) { enlarge_threshold_ = t; } + size_type shrink_threshold() const { return shrink_threshold_; } + void set_shrink_threshold(size_type t) { shrink_threshold_ = t; } + + size_type enlarge_size(size_type x) const { return static_cast(x * enlarge_factor_); } + size_type shrink_size(size_type x) const { return static_cast(x * shrink_factor_); } + + bool consider_shrink() const { return consider_shrink_; } + void set_consider_shrink(bool t) { consider_shrink_ = t; } + + unsigned int num_ht_copies() const { return num_ht_copies_; } + void inc_num_ht_copies() { ++num_ht_copies_; } + + // Reset the enlarge and shrink thresholds + void reset_thresholds(size_type num_buckets) + { + set_enlarge_threshold(enlarge_size(num_buckets)); + set_shrink_threshold(shrink_size(num_buckets)); + // whatever caused us to reset already considered + set_consider_shrink(false); + } + + // Caller is resposible for calling reset_threshold right after + // set_resizing_parameters. + // ------------------------------------------------------------ + void set_resizing_parameters(float shrink, float grow) + { + assert(shrink >= 0); + assert(grow <= 1); + if (shrink > grow/2.0f) + shrink = grow / 2.0f; // otherwise we thrash hashtable size + set_shrink_factor(shrink); + set_enlarge_factor(grow); + } + + // This is the smallest size a hashtable can be without being too crowded + // If you like, you can give a min #buckets as well as a min #elts + // ---------------------------------------------------------------------- + size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) + { + float enlarge = enlarge_factor(); + size_type sz = HT_MIN_BUCKETS; // min buckets allowed + while (sz < min_buckets_wanted || + num_elts >= static_cast(sz * enlarge)) + { + // This just prevents overflowing size_type, since sz can exceed + // max_size() here. + // ------------------------------------------------------------- + if (static_cast(sz * 2) < sz) + throw_exception(std::length_error("resize overflow")); // protect against overflow + sz *= 2; + } + return sz; + } + + private: + size_type enlarge_threshold_; // table.size() * enlarge_factor + size_type shrink_threshold_; // table.size() * shrink_factor + float enlarge_factor_; // how full before resize + float shrink_factor_; // how empty before resize + bool consider_shrink_; // if we should try to shrink before next insert + + unsigned int num_ht_copies_; // num_ht_copies is a counter incremented every Copy/Move + }; + +} // namespace sparsehash_internal + +#undef SPP_COMPILE_ASSERT + +// ---------------------------------------------------------------------- +// S P A R S E T A B L E +// ---------------------------------------------------------------------- +// +// A sparsetable is a random container that implements a sparse array, +// that is, an array that uses very little memory to store unassigned +// indices (in this case, between 1-2 bits per unassigned index). For +// instance, if you allocate an array of size 5 and assign a[2] = , then a[2] will take up a lot of memory but a[0], a[1], +// a[3], and a[4] will not. Array elements that have a value are +// called "assigned". Array elements that have no value yet, or have +// had their value cleared using erase() or clear(), are called +// "unassigned". +// +// Unassigned values seem to have the default value of T (see below). +// Nevertheless, there is a difference between an unassigned index and +// one explicitly assigned the value of T(). The latter is considered +// assigned. +// +// Access to an array element is constant time, as is insertion and +// deletion. Insertion and deletion may be fairly slow, however: +// because of this container's memory economy, each insert and delete +// causes a memory reallocation. +// +// NOTE: You should not test(), get(), or set() any index that is +// greater than sparsetable.size(). If you need to do that, call +// resize() first. +// +// --- Template parameters +// PARAMETER DESCRIPTION DEFAULT +// T The value of the array: the type of -- +// object that is stored in the array. +// +// Alloc: Allocator to use to allocate memory. +// +// --- Model of +// Random Access Container +// +// --- Type requirements +// T must be Copy Constructible. It need not be Assignable. +// +// --- Public base classes +// None. +// +// --- Members +// +// [*] All iterators are const in a sparsetable (though nonempty_iterators +// may not be). Use get() and set() to assign values, not iterators. +// +// [+] iterators are random-access iterators. nonempty_iterators are +// bidirectional iterators. + +// [*] If you shrink a sparsetable using resize(), assigned elements +// past the end of the table are removed using erase(). If you grow +// a sparsetable, new unassigned indices are created. +// +// [+] Note that operator[] returns a const reference. You must use +// set() to change the value of a table element. +// +// [!] Unassignment also calls the destructor. +// +// Iterators are invalidated whenever an item is inserted or +// deleted (ie set() or erase() is used) or when the size of +// the table changes (ie resize() or clear() is used). + + + +// --------------------------------------------------------------------------- +// Our iterator as simple as iterators can be: basically it's just +// the index into our table. Dereference, the only complicated +// thing, we punt to the table class. This just goes to show how +// much machinery STL requires to do even the most trivial tasks. +// +// A NOTE ON ASSIGNING: +// A sparse table does not actually allocate memory for entries +// that are not filled. Because of this, it becomes complicated +// to have a non-const iterator: we don't know, if the iterator points +// to a not-filled bucket, whether you plan to fill it with something +// or whether you plan to read its value (in which case you'll get +// the default bucket value). Therefore, while we can define const +// operations in a pretty 'normal' way, for non-const operations, we +// define something that returns a helper object with operator= and +// operator& that allocate a bucket lazily. We use this for table[] +// and also for regular table iterators. + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// Our iterator as simple as iterators can be: basically it's just +// the index into our table. Dereference, the only complicated +// thing, we punt to the table class. This just goes to show how +// much machinery STL requires to do even the most trivial tasks. +// +// By templatizing over tabletype, we have one iterator type which +// we can use for both sparsetables and sparsebins. In fact it +// works on any class that allows size() and operator[] (eg vector), +// as long as it does the standard STL typedefs too (eg value_type). + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +class table_iterator +{ +public: + typedef table_iterator iterator; + + typedef std::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + + explicit table_iterator(tabletype *tbl = 0, size_type p = 0) : + table(tbl), pos(p) + { } + + // Helper function to assert things are ok; eg pos is still in range + void check() const + { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + iterator& operator+=(size_type t) { pos += t; check(); return *this; } + iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + iterator& operator++() { ++pos; check(); return *this; } + iterator& operator--() { --pos; check(); return *this; } + iterator operator++(int) + { + iterator tmp(*this); // for x++ + ++pos; check(); return tmp; + } + + iterator operator--(int) + { + iterator tmp(*this); // for x-- + --pos; check(); return tmp; + } + + iterator operator+(difference_type i) const + { + iterator tmp(*this); + tmp += i; return tmp; + } + + iterator operator-(difference_type i) const + { + iterator tmp(*this); + tmp -= i; return tmp; + } + + difference_type operator-(iterator it) const + { + // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + + // Comparisons. + bool operator==(const iterator& it) const + { + return table == it.table && pos == it.pos; + } + + bool operator<(const iterator& it) const + { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + + bool operator!=(const iterator& it) const { return !(*this == it); } + bool operator<=(const iterator& it) const { return !(it < *this); } + bool operator>(const iterator& it) const { return it < *this; } + bool operator>=(const iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +class const_table_iterator +{ +public: + typedef table_iterator iterator; + typedef const_table_iterator const_iterator; + + typedef std::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + typedef typename tabletype::const_reference reference; // we're const-only + typedef typename tabletype::const_pointer pointer; + + // The "real" constructor + const_table_iterator(const tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + + // The default constructor, used when I define vars of type table::iterator + const_table_iterator() : table(NULL), pos(0) { } + + // The copy constructor, for when I say table::iterator foo = tbl.begin() + // Also converts normal iterators to const iterators // not explicit on purpose + const_table_iterator(const iterator &from) + : table(from.table), pos(from.pos) { } + + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // The main thing our iterator does is dereference. If the table entry + // we point to is empty, we return the default value type. + reference operator*() const { return (*table)[pos]; } + pointer operator->() const { return &(operator*()); } + + // Helper function to assert things are ok; eg pos is still in range + void check() const + { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + const_iterator& operator+=(size_type t) { pos += t; check(); return *this; } + const_iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + const_iterator& operator++() { ++pos; check(); return *this; } + const_iterator& operator--() { --pos; check(); return *this; } + const_iterator operator++(int) + { + const_iterator tmp(*this); // for x++ + ++pos; check(); + return tmp; + } + const_iterator operator--(int) + { + const_iterator tmp(*this); // for x-- + --pos; check(); + return tmp; + } + const_iterator operator+(difference_type i) const + { + const_iterator tmp(*this); + tmp += i; + return tmp; + } + const_iterator operator-(difference_type i) const + { + const_iterator tmp(*this); + tmp -= i; + return tmp; + } + difference_type operator-(const_iterator it) const + { + // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + reference operator[](difference_type n) const + { + return *(*this + n); // simple though not totally efficient + } + + // Comparisons. + bool operator==(const const_iterator& it) const + { + return table == it.table && pos == it.pos; + } + + bool operator<(const const_iterator& it) const + { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + bool operator!=(const const_iterator& it) const { return !(*this == it); } + bool operator<=(const const_iterator& it) const { return !(it < *this); } + bool operator>(const const_iterator& it) const { return it < *this; } + bool operator>=(const const_iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + const tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// --------------------------------------------------------------------------- +// This is a 2-D iterator. You specify a begin and end over a list +// of *containers*. We iterate over each container by iterating over +// it. It's actually simple: +// VECTOR.begin() VECTOR[0].begin() --------> VECTOR[0].end() ---, +// | ________________________________________________/ +// | \_> VECTOR[1].begin() --------> VECTOR[1].end() -, +// | ___________________________________________________/ +// v \_> ...... +// VECTOR.end() +// +// It's impossible to do random access on one of these things in constant +// time, so it's just a bidirectional iterator. +// +// Unfortunately, because we need to use this for a non-empty iterator, +// we use ne_begin() and ne_end() instead of begin() and end() +// (though only going across, not down). +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +class Two_d_iterator +{ +public: + typedef Two_d_iterator iterator; + typedef iter_type iterator_category; + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + + explicit Two_d_iterator(row_it curr) : row_current(curr), col_current(0) + { + if (row_current && !row_current->is_marked()) + { + col_current = row_current->ne_begin(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + + explicit Two_d_iterator(row_it curr, col_it col) : row_current(curr), col_current(col) + { + assert(col); + } + + // The default constructor + Two_d_iterator() : row_current(0), col_current(0) { } + + // Need this explicitly so we can convert normal iterators <=> const iterators + // not explicit on purpose + // --------------------------------------------------------------------------- + template + Two_d_iterator(const Two_d_iterator& it) : + row_current (*(row_it *)&it.row_current), + col_current (*(col_it *)&it.col_current) + { } + + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + value_type& operator*() const { return *(col_current); } + value_type* operator->() const { return &(operator*()); } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + // NOTE: this is not amortized constant time! What do we do about it? + // ------------------------------------------------------------------ + void advance_past_end() + { + // used when col_current points to end() + while (col_current == row_current->ne_end()) + { + // end of current row + // ------------------ + ++row_current; // go to beginning of next + if (!row_current->is_marked()) // col is irrelevant at end + col_current = row_current->ne_begin(); + else + break; // don't go past row_end + } + } + + friend size_t operator-(iterator l, iterator f) + { + if (f.row_current->is_marked()) + return 0; + + size_t diff(0); + while (f != l) + { + ++diff; + ++f; + } + return diff; + } + + iterator& operator++() + { + // assert(!row_current->is_marked()); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + + iterator& operator--() + { + while (row_current->is_marked() || + col_current == row_current->ne_begin()) + { + --row_current; + col_current = row_current->ne_end(); // this is 1 too far + } + --col_current; + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } + + + // Comparisons. + bool operator==(const iterator& it) const + { + return (row_current == it.row_current && + (!row_current || row_current->is_marked() || col_current == it.col_current)); + } + + bool operator!=(const iterator& it) const { return !(*this == it); } + + // Here's the info we actually need to be an iterator + // These need to be public so we convert from iterator to const_iterator + // --------------------------------------------------------------------- + row_it row_current; + col_it col_current; +}; + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +class Two_d_destructive_iterator : public Two_d_iterator +{ +public: + typedef Two_d_destructive_iterator iterator; + + Two_d_destructive_iterator(Alloc &alloc, row_it curr) : + _alloc(alloc) + { + this->row_current = curr; + this->col_current = 0; + if (this->row_current && !this->row_current->is_marked()) + { + this->col_current = this->row_current->ne_begin(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + // NOTE: this is not amortized constant time! What do we do about it? + // ------------------------------------------------------------------ + void advance_past_end() + { + // used when col_current points to end() + while (this->col_current == this->row_current->ne_end()) + { + this->row_current->clear(_alloc, true); // This is what differs from non-destructive iterators above + + // end of current row + // ------------------ + ++this->row_current; // go to beginning of next + if (!this->row_current->is_marked()) // col is irrelevant at end + this->col_current = this->row_current->ne_begin(); + else + break; // don't go past row_end + } + } + + iterator& operator++() + { + // assert(!this->row_current->is_marked()); // how to ++ from there? + ++this->col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + +private: + Two_d_destructive_iterator& operator=(const Two_d_destructive_iterator &o); + + Alloc &_alloc; +}; + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +#if defined(SPP_POPCNT_CHECK) +static inline bool spp_popcount_check() +{ + int cpuInfo[4] = { -1 }; + spp_cpuid(cpuInfo, 1); + if (cpuInfo[2] & (1 << 23)) + return true; // means SPP_POPCNT supported + return false; +} +#endif + +#if defined(SPP_POPCNT_CHECK) && defined(SPP_POPCNT) + +static inline uint32_t spp_popcount(uint32_t i) +{ + static const bool s_ok = spp_popcount_check(); + return s_ok ? SPP_POPCNT(i) : s_spp_popcount_default(i); +} + +#else + +static inline uint32_t spp_popcount(uint32_t i) +{ +#if defined(SPP_POPCNT) + return static_cast(SPP_POPCNT(i)); +#else + return s_spp_popcount_default(i); +#endif +} + +#endif + +#if defined(SPP_POPCNT_CHECK) && defined(SPP_POPCNT64) + +static inline uint32_t spp_popcount(uint64_t i) +{ + static const bool s_ok = spp_popcount_check(); + return s_ok ? (uint32_t)SPP_POPCNT64(i) : s_spp_popcount_default(i); +} + +#else + +static inline uint32_t spp_popcount(uint64_t i) +{ +#if defined(SPP_POPCNT64) + return static_cast(SPP_POPCNT64(i)); +#elif 1 + return s_spp_popcount_default(i); +#endif +} + +#endif + +// --------------------------------------------------------------------------- +// SPARSE-TABLE +// ------------ +// The idea is that a table with (logically) t buckets is divided +// into t/M *groups* of M buckets each. (M is a constant, typically +// 32) Each group is stored sparsely. +// Thus, inserting into the table causes some array to grow, which is +// slow but still constant time. Lookup involves doing a +// logical-position-to-sparse-position lookup, which is also slow but +// constant time. The larger M is, the slower these operations are +// but the less overhead (slightly). +// +// To store the sparse array, we store a bitmap B, where B[i] = 1 iff +// bucket i is non-empty. Then to look up bucket i we really look up +// array[# of 1s before i in B]. This is constant time for fixed M. +// +// Terminology: the position of an item in the overall table (from +// 1 .. t) is called its "location." The logical position in a group +// (from 1 .. M) is called its "position." The actual location in +// the array (from 1 .. # of non-empty buckets in the group) is +// called its "offset." +// --------------------------------------------------------------------------- + +template +class sparsegroup +{ +public: + // Basic types + typedef T value_type; + typedef Alloc allocator_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + typedef uint8_t size_type; // max # of buckets + + // These are our special iterators, that go over non-empty buckets in a + // group. These aren't const-only because you can change non-empty bcks. + // --------------------------------------------------------------------- + typedef pointer ne_iterator; + typedef const_pointer const_ne_iterator; + typedef std::reverse_iterator reverse_ne_iterator; + typedef std::reverse_iterator const_reverse_ne_iterator; + + // We'll have versions for our special non-empty iterator too + // ---------------------------------------------------------- + ne_iterator ne_begin() { return reinterpret_cast(_group); } + const_ne_iterator ne_begin() const { return reinterpret_cast(_group); } + const_ne_iterator ne_cbegin() const { return reinterpret_cast(_group); } + ne_iterator ne_end() { return reinterpret_cast(_group + _num_items()); } + const_ne_iterator ne_end() const { return reinterpret_cast(_group + _num_items()); } + const_ne_iterator ne_cend() const { return reinterpret_cast(_group + _num_items()); } + reverse_ne_iterator ne_rbegin() { return reverse_ne_iterator(ne_end()); } + const_reverse_ne_iterator ne_rbegin() const { return const_reverse_ne_iterator(ne_cend()); } + const_reverse_ne_iterator ne_crbegin() const { return const_reverse_ne_iterator(ne_cend()); } + reverse_ne_iterator ne_rend() { return reverse_ne_iterator(ne_begin()); } + const_reverse_ne_iterator ne_rend() const { return const_reverse_ne_iterator(ne_cbegin()); } + const_reverse_ne_iterator ne_crend() const { return const_reverse_ne_iterator(ne_cbegin()); } + +private: + // T can be std::pair, but sometime we need to cast to a mutable type + // ------------------------------------------------------------------------------ + typedef typename spp_::cvt::type mutable_value_type; + typedef mutable_value_type & mutable_reference; + typedef mutable_value_type * mutable_pointer; + typedef const mutable_value_type * const_mutable_pointer; + + bool _bmtest(size_type i) const { return !!(_bitmap & (static_cast(1) << i)); } + void _bmset(size_type i) { _bitmap |= static_cast(1) << i; } + void _bmclear(size_type i) { _bitmap &= ~(static_cast(1) << i); } + + bool _bme_test(size_type i) const { return !!(_bm_erased & (static_cast(1) << i)); } + void _bme_set(size_type i) { _bm_erased |= static_cast(1) << i; } + void _bme_clear(size_type i) { _bm_erased &= ~(static_cast(1) << i); } + + bool _bmtest_strict(size_type i) const + { return !!((_bitmap | _bm_erased) & (static_cast(1) << i)); } + + + static uint32_t _sizing(uint32_t n) + { +#if !defined(SPP_ALLOC_SZ) || (SPP_ALLOC_SZ == 0) + // aggressive allocation first, then decreasing as sparsegroups fill up + // -------------------------------------------------------------------- + struct alloc_batch_size + { + // 32 bit bitmap + // ........ .... .... .. .. .. .. . . . . . . . . + // 8 12 16 18 20 22 24 25 26 ... 32 + // ------------------------------------------------------ + SPP_CXX14_CONSTEXPR alloc_batch_size() + : data() + { + uint8_t group_sz = SPP_GROUP_SIZE / 4; + uint8_t group_start_alloc = SPP_GROUP_SIZE / 8; //4; + uint8_t alloc_sz = group_start_alloc; + for (int i=0; i<4; ++i) + { + for (int j=0; j 2) + group_start_alloc /= 2; + alloc_sz += group_start_alloc; + } + } + uint8_t data[SPP_GROUP_SIZE]; + }; + + static alloc_batch_size s_alloc_batch_sz; + return n ? static_cast(s_alloc_batch_sz.data[n-1]) : 0; // more aggressive alloc at the beginning + +#elif (SPP_ALLOC_SZ == 1) + // use as little memory as possible - slowest insert/delete in table + // ----------------------------------------------------------------- + return n; +#else + // decent compromise when SPP_ALLOC_SZ == 2 + // ---------------------------------------- + static size_type sz_minus_1 = SPP_ALLOC_SZ - 1; + return (n + sz_minus_1) & ~sz_minus_1; +#endif + } + + pointer _allocate_group(allocator_type &alloc, uint32_t n /* , bool tight = false */) + { + // ignore tight since we don't store num_alloc + // num_alloc = (uint8_t)(tight ? n : _sizing(n)); + + uint32_t num_alloc = (uint8_t)_sizing(n); + _set_num_alloc(num_alloc); + pointer retval = alloc.allocate(static_cast(num_alloc)); + if (retval == NULL) + { + // the allocator is supposed to throw an exception if the allocation fails. + throw_exception(std::bad_alloc()); + } + return retval; + } + + void _free_group(allocator_type &alloc, uint32_t num_alloc) + { + if (_group) + { + uint32_t num_buckets = _num_items(); + if (num_buckets) + { + mutable_pointer end_it = (mutable_pointer)(_group + num_buckets); + for (mutable_pointer p = (mutable_pointer)_group; p != end_it; ++p) + p->~mutable_value_type(); + } + alloc.deallocate(_group, (typename allocator_type::size_type)num_alloc); + _group = NULL; + } + } + + // private because should not be called - no allocator! + sparsegroup &operator=(const sparsegroup& x); + + static size_type _pos_to_offset(group_bm_type bm, size_type pos) + { + //return (size_type)((uint32_t)~((int32_t(-1) + pos) >> 31) & spp_popcount(bm << (SPP_GROUP_SIZE - pos))); + //return (size_type)(pos ? spp_popcount(bm << (SPP_GROUP_SIZE - pos)) : 0); + return static_cast(spp_popcount(bm & ((static_cast(1) << pos) - 1))); + } + +public: + + // get_iter() in sparsetable needs it + size_type pos_to_offset(size_type pos) const + { + return _pos_to_offset(_bitmap, pos); + } + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4146) +#endif + + // Returns the (logical) position in the bm[] array, i, such that + // bm[i] is the offset-th set bit in the array. It is the inverse + // of pos_to_offset. get_pos() uses this function to find the index + // of an ne_iterator in the table. Bit-twiddling from + // http://hackersdelight.org/basics.pdf + // ----------------------------------------------------------------- + static size_type offset_to_pos(group_bm_type bm, size_type offset) + { + for (; offset > 0; offset--) + bm &= (bm-1); // remove right-most set bit + + // Clear all bits to the left of the rightmost bit (the &), + // and then clear the rightmost bit but set all bits to the + // right of it (the -1). + // -------------------------------------------------------- + bm = (bm & -bm) - 1; + return static_cast(spp_popcount(bm)); + } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + size_type offset_to_pos(size_type offset) const + { + return offset_to_pos(_bitmap, offset); + } + +public: + // Constructors -- default and copy -- and destructor + explicit sparsegroup() : + _group(0), _bitmap(0), _bm_erased(0) + { + _set_num_items(0); + _set_num_alloc(0); + } + + sparsegroup(const sparsegroup& x) : + _group(0), _bitmap(x._bitmap), _bm_erased(x._bm_erased) + { + _set_num_items(0); + _set_num_alloc(0); + assert(_group == 0); + } + + sparsegroup(const sparsegroup& x, allocator_type& a) : + _group(0), _bitmap(x._bitmap), _bm_erased(x._bm_erased) + { + _set_num_items(0); + _set_num_alloc(0); + + uint32_t num_items = x._num_items(); + if (num_items) + { + _group = _allocate_group(a, num_items /* , true */); + _set_num_items(num_items); + std::uninitialized_copy(x._group, x._group + num_items, _group); + } + } + + ~sparsegroup() { assert(_group == 0); } + + void destruct(allocator_type& a) { _free_group(a, _num_alloc()); } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsegroup& x) + { + using std::swap; + + swap(_group, x._group); + swap(_bitmap, x._bitmap); + swap(_bm_erased, x._bm_erased); +#ifdef SPP_STORE_NUM_ITEMS + swap(_num_buckets, x._num_buckets); + swap(_num_allocated, x._num_allocated); +#endif + } + + // It's always nice to be able to clear a table without deallocating it + void clear(allocator_type &alloc, bool erased) + { + _free_group(alloc, _num_alloc()); + _bitmap = 0; + if (erased) + _bm_erased = 0; + _set_num_items(0); + _set_num_alloc(0); + } + + // Functions that tell you about size. Alas, these aren't so useful + // because our table is always fixed size. + size_type size() const { return static_cast(SPP_GROUP_SIZE); } + size_type max_size() const { return static_cast(SPP_GROUP_SIZE); } + + bool empty() const { return false; } + + // We also may want to know how many *used* buckets there are + size_type num_nonempty() const { return (size_type)_num_items(); } + + // TODO(csilvers): make protected + friend + // This is used by sparse_hashtable to get an element from the table + // when we know it exists. + reference unsafe_get(size_type i) const + { + // assert(_bmtest(i)); + return (reference)_group[pos_to_offset(i)]; + } + + typedef std::pair SetResult; + +private: + //typedef spp_::integral_constant::value> check_relocatable; + typedef spp_::true_type realloc_ok_type; + typedef spp_::false_type realloc_not_ok_type; + + //typedef spp_::zero_type libc_reloc_type; + //typedef spp_::one_type spp_reloc_type; + //typedef spp_::two_type spp_not_reloc_type; + //typedef spp_::three_type generic_alloc_type; + +#if 1 + typedef typename if_<((spp_::is_same >::value || + spp_::is_same >::value) && + spp_::is_relocatable::value), realloc_ok_type, realloc_not_ok_type>::type + check_alloc_type; +#else + typedef typename if_ >::value, + typename if_::value, spp_reloc_type, spp_not_reloc_type>::type, + typename if_<(spp_::is_same >::value && + spp_::is_relocatable::value), libc_reloc_type, generic_alloc_type>::type >::type + check_alloc_type; +#endif + + + //typedef if_ >::value, + // libc_alloc_type, + // if_ >::value, + // spp_alloc_type, user_alloc_type> > check_alloc_type; + + //typedef spp_::integral_constant::value && + // (spp_::is_same >::value || + // spp_::is_same >::value)) > + // realloc_and_memmove_ok; + + // ------------------------- memory at *p is uninitialized => need to construct + void _init_val(mutable_value_type *p, reference val) + { +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + ::new (p) value_type(std::move((mutable_reference)val)); +#else + ::new (p) value_type((mutable_reference)val); +#endif + } + + // ------------------------- memory at *p is uninitialized => need to construct + void _init_val(mutable_value_type *p, const_reference val) + { + ::new (p) value_type(val); + } + + // ------------------------------------------------ memory at *p is initialized + void _set_val(value_type *p, reference val) + { +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + *(mutable_pointer)p = std::move((mutable_reference)val); +#else + using std::swap; + swap(*(mutable_pointer)p, *(mutable_pointer)&val); +#endif + } + + // ------------------------------------------------ memory at *p is initialized + void _set_val(value_type *p, const_reference val) + { + *(mutable_pointer)p = *(const_mutable_pointer)&val; + } + + // Create space at _group[offset], assuming value_type is relocatable, and the + // allocator_type is the spp allocator. + // return true if the slot was constructed (i.e. contains a valid value_type + // --------------------------------------------------------------------------------- + template + void _set_aux(allocator_type &alloc, size_type offset, Val &val, realloc_ok_type) + { + //static int x=0; if (++x < 10) printf("x\n"); // check we are getting here + + uint32_t num_items = _num_items(); + uint32_t num_alloc = _sizing(num_items); + + if (num_items == num_alloc) + { + num_alloc = _sizing(num_items + 1); + _group = alloc.reallocate(_group, num_alloc); + _set_num_alloc(num_alloc); + } + + for (uint32_t i = num_items; i > offset; --i) + memcpy(static_cast(_group + i), _group + i-1, sizeof(*_group)); + + _init_val((mutable_pointer)(_group + offset), val); + } + + // Create space at _group[offset], assuming value_type is *not* relocatable, and the + // allocator_type is the spp allocator. + // return true if the slot was constructed (i.e. contains a valid value_type + // --------------------------------------------------------------------------------- + template + void _set_aux(allocator_type &alloc, size_type offset, Val &val, realloc_not_ok_type) + { + uint32_t num_items = _num_items(); + uint32_t num_alloc = _sizing(num_items); + + //assert(num_alloc == (uint32_t)_num_allocated); + if (num_items < num_alloc) + { + // create new object at end and rotate it to position + _init_val((mutable_pointer)&_group[num_items], val); + std::rotate((mutable_pointer)(_group + offset), + (mutable_pointer)(_group + num_items), + (mutable_pointer)(_group + num_items + 1)); + return; + } + + // This is valid because 0 <= offset <= num_items + pointer p = _allocate_group(alloc, _sizing(num_items + 1)); + if (offset) + std::uninitialized_copy(MK_MOVE_IT((mutable_pointer)_group), + MK_MOVE_IT((mutable_pointer)(_group + offset)), + (mutable_pointer)p); + if (num_items > offset) + std::uninitialized_copy(MK_MOVE_IT((mutable_pointer)(_group + offset)), + MK_MOVE_IT((mutable_pointer)(_group + num_items)), + (mutable_pointer)(p + offset + 1)); + _init_val((mutable_pointer)(p + offset), val); + _free_group(alloc, num_alloc); + _group = p; + } + + // ---------------------------------------------------------------------------------- + template + void _set(allocator_type &alloc, size_type i, size_type offset, Val &val) + { + if (!_bmtest(i)) + { + _set_aux(alloc, offset, val, check_alloc_type()); + _incr_num_items(); + _bmset(i); + } + else + _set_val(&_group[offset], val); + } + +public: + + // This returns the pointer to the inserted item + // --------------------------------------------- + template + pointer set(allocator_type &alloc, size_type i, Val &val) + { + _bme_clear(i); // in case this was an "erased" location + + size_type offset = pos_to_offset(i); + _set(alloc, i, offset, val); // may change _group pointer + return (pointer)(_group + offset); + } + + // We let you see if a bucket is non-empty without retrieving it + // ------------------------------------------------------------- + bool test(size_type i) const + { + return _bmtest(i); + } + + // also tests for erased values + // ---------------------------- + bool test_strict(size_type i) const + { + return _bmtest_strict(i); + } + +private: + // Shrink the array, assuming value_type is relocatable, and the + // allocator_type is the libc allocator (supporting reallocate). + // ------------------------------------------------------------- + void _group_erase_aux(allocator_type &alloc, size_type offset, realloc_ok_type) + { + // static int x=0; if (++x < 10) printf("Y\n"); // check we are getting here + uint32_t num_items = _num_items(); + uint32_t num_alloc = _sizing(num_items); + + if (num_items == 1) + { + assert(offset == 0); + _free_group(alloc, num_alloc); + _set_num_alloc(0); + return; + } + + _group[offset].~value_type(); + + for (size_type i = offset; i < num_items - 1; ++i) + memcpy(static_cast(_group + i), _group + i + 1, sizeof(*_group)); + + if (_sizing(num_items - 1) != num_alloc) + { + num_alloc = _sizing(num_items - 1); + assert(num_alloc); // because we have at least 1 item left + _set_num_alloc(num_alloc); + _group = alloc.reallocate(_group, num_alloc); + } + } + + // Shrink the array, without any special assumptions about value_type and + // allocator_type. + // -------------------------------------------------------------------------- + void _group_erase_aux(allocator_type &alloc, size_type offset, realloc_not_ok_type) + { + uint32_t num_items = _num_items(); + uint32_t num_alloc = _sizing(num_items); + + if (_sizing(num_items - 1) != num_alloc) + { + pointer p = 0; + if (num_items > 1) + { + p = _allocate_group(alloc, num_items - 1); + if (offset) + std::uninitialized_copy(MK_MOVE_IT((mutable_pointer)(_group)), + MK_MOVE_IT((mutable_pointer)(_group + offset)), + (mutable_pointer)(p)); + if (static_cast(offset + 1) < num_items) + std::uninitialized_copy(MK_MOVE_IT((mutable_pointer)(_group + offset + 1)), + MK_MOVE_IT((mutable_pointer)(_group + num_items)), + (mutable_pointer)(p + offset)); + } + else + { + assert(offset == 0); + _set_num_alloc(0); + } + _free_group(alloc, num_alloc); + _group = p; + } + else + { + std::rotate((mutable_pointer)(_group + offset), + (mutable_pointer)(_group + offset + 1), + (mutable_pointer)(_group + num_items)); + ((mutable_pointer)(_group + num_items - 1))->~mutable_value_type(); + } + } + + void _group_erase(allocator_type &alloc, size_type offset) + { + _group_erase_aux(alloc, offset, check_alloc_type()); + } + +public: + template + bool erase_ne(allocator_type &alloc, twod_iter &it) + { + assert(_group && it.col_current != ne_end()); + size_type offset = (size_type)(it.col_current - ne_begin()); + size_type pos = offset_to_pos(offset); + + if (_num_items() <= 1) + { + clear(alloc, false); + it.col_current = 0; + } + else + { + _group_erase(alloc, offset); + _decr_num_items(); + _bmclear(pos); + + // in case _group_erase reallocated the buffer + it.col_current = reinterpret_cast(_group) + offset; + } + _bme_set(pos); // remember that this position has been erased + it.advance_past_end(); + return true; + } + + + // This takes the specified elements out of the group. This is + // "undefining", rather than "clearing". + // TODO(austern): Make this exception safe: handle exceptions from + // value_type's copy constructor. + // --------------------------------------------------------------- + void erase(allocator_type &alloc, size_type i) + { + if (_bmtest(i)) + { + // trivial to erase empty bucket + if (_num_items() == 1) + clear(alloc, false); + else + { + _group_erase(alloc, pos_to_offset(i)); + _decr_num_items(); + _bmclear(i); + } + _bme_set(i); // remember that this position has been erased + } + } + + // I/O + // We support reading and writing groups to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the bitmap and size. Meant to be used with table I/O. + // -------------------------------------------------------------- + template bool write_metadata(OUTPUT *fp) const + { + // warning: we write 4 or 8 bytes for the bitmap, instead of 6 in the + // original google sparsehash + // ------------------------------------------------------------------ + if (!sparsehash_internal::write_data(fp, &_bitmap, sizeof(_bitmap))) + return false; + + return true; + } + + // Reading destroys the old group contents! Returns true if all was ok. + template bool read_metadata(allocator_type &alloc, INPUT *fp) + { + clear(alloc, true); + + if (!sparsehash_internal::read_data(fp, &_bitmap, sizeof(_bitmap))) + return false; + + // We'll allocate the space, but we won't fill it: it will be + // left as uninitialized raw memory. + uint32_t num_items = spp_popcount(_bitmap); // yes, _num_buckets not set + _set_num_items(num_items); + _group = num_items ? _allocate_group(alloc, num_items/* , true */) : 0; + return true; + } + + // Again, only meaningful if value_type is a POD. + template bool read_nopointer_data(INPUT *fp) + { + for (ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!sparsehash_internal::read_data(fp, &(*it), sizeof(*it))) + return false; + return true; + } + + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means POD and no pointers. + // However, we don't try to normalize endianness. + // ------------------------------------------------------------ + template bool write_nopointer_data(OUTPUT *fp) const + { + for (const_ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!sparsehash_internal::write_data(fp, &(*it), sizeof(*it))) + return false; + return true; + } + + + // Comparisons. We only need to define == and < -- we get + // != > <= >= via relops.h (which we happily included above). + // Note the comparisons are pretty arbitrary: we compare + // values of the first index that isn't equal (using default + // value for empty buckets). + // --------------------------------------------------------- + bool operator==(const sparsegroup& x) const + { + return (_bitmap == x._bitmap && + _bm_erased == x._bm_erased && + std::equal(_group, _group + _num_items(), x._group)); + } + + bool operator<(const sparsegroup& x) const + { + // also from + return std::lexicographical_compare(_group, _group + _num_items(), + x._group, x._group + x._num_items()); + } + + bool operator!=(const sparsegroup& x) const { return !(*this == x); } + bool operator<=(const sparsegroup& x) const { return !(x < *this); } + bool operator> (const sparsegroup& x) const { return x < *this; } + bool operator>=(const sparsegroup& x) const { return !(*this < x); } + + void mark() { _group = (value_type *)static_cast(-1); } + bool is_marked() const { return _group == (value_type *)static_cast(-1); } + +private: + // --------------------------------------------------------------------------- + template + class alloc_impl : public A + { + public: + typedef typename A::pointer pointer; + typedef typename A::size_type size_type; + + // Convert a normal allocator to one that has realloc_or_die() + explicit alloc_impl(const A& a) : A(a) { } + + // realloc_or_die should only be used when using the default + // allocator (spp::spp_allocator). + pointer realloc_or_die(pointer /*ptr*/, size_type /*n*/) + { + throw_exception(std::runtime_error("realloc_or_die is only supported for spp::spp_allocator\n")); + return NULL; + } + }; + + // A template specialization of alloc_impl for + // spp::libc_allocator that can handle realloc_or_die. + // ----------------------------------------------------------- + template + class alloc_impl > : public spp_::libc_allocator + { + public: + typedef typename spp_::libc_allocator::pointer pointer; + typedef typename spp_::libc_allocator::size_type size_type; + + explicit alloc_impl(const spp_::libc_allocator& a) + : spp_::libc_allocator(a) + { } + + pointer realloc_or_die(pointer ptr, size_type n) + { + pointer retval = this->reallocate(ptr, n); + if (retval == NULL) + { + // the allocator is supposed to throw an exception if the allocation fails. + throw_exception(std::bad_alloc()); + } + return retval; + } + }; + + // A template specialization of alloc_impl for + // spp::spp_allocator that can handle realloc_or_die. + // ----------------------------------------------------------- + template + class alloc_impl > : public spp_::spp_allocator + { + public: + typedef typename spp_::spp_allocator::pointer pointer; + typedef typename spp_::spp_allocator::size_type size_type; + + explicit alloc_impl(const spp_::spp_allocator& a) + : spp_::spp_allocator(a) + { } + + pointer realloc_or_die(pointer ptr, size_type n) + { + pointer retval = this->reallocate(ptr, n); + if (retval == NULL) + { + // the allocator is supposed to throw an exception if the allocation fails. + throw_exception(std::bad_alloc()); + } + return retval; + } + }; + + +#ifdef SPP_STORE_NUM_ITEMS + uint32_t _num_items() const { return (uint32_t)_num_buckets; } + void _set_num_items(uint32_t val) { _num_buckets = static_cast(val); } + void _incr_num_items() { ++_num_buckets; } + void _decr_num_items() { --_num_buckets; } + uint32_t _num_alloc() const { return (uint32_t)_num_allocated; } + void _set_num_alloc(uint32_t val) { _num_allocated = static_cast(val); } +#else + uint32_t _num_items() const { return spp_popcount(_bitmap); } + void _set_num_items(uint32_t ) { } + void _incr_num_items() { } + void _decr_num_items() { } + uint32_t _num_alloc() const { return _sizing(_num_items()); } + void _set_num_alloc(uint32_t val) { } +#endif + + // The actual data + // --------------- + value_type * _group; // (small) array of T's + group_bm_type _bitmap; + group_bm_type _bm_erased; // ones where items have been erased + +#ifdef SPP_STORE_NUM_ITEMS + size_type _num_buckets; + size_type _num_allocated; +#endif +}; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +template +class sparsetable +{ +public: + typedef T value_type; + typedef Alloc allocator_type; + typedef sparsegroup group_type; + +private: + typedef typename Alloc::template rebind::other group_alloc_type; + typedef typename group_alloc_type::size_type group_size_type; + +public: + // Basic types + // ----------- + typedef typename allocator_type::size_type size_type; + typedef typename allocator_type::difference_type difference_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + typedef group_type& GroupsReference; + typedef const group_type& GroupsConstReference; + + typedef typename group_type::ne_iterator ColIterator; + typedef typename group_type::const_ne_iterator ColConstIterator; + + typedef table_iterator > iterator; // defined with index + typedef const_table_iterator > const_iterator; // defined with index + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + // These are our special iterators, that go over non-empty buckets in a + // table. These aren't const only because you can change non-empty bcks. + // ---------------------------------------------------------------------- + typedef Two_d_iterator ne_iterator; + + typedef Two_d_iterator const_ne_iterator; + + // Another special iterator: it frees memory as it iterates (used to resize). + // Obviously, you can only iterate over it once, which is why it's an input iterator + // --------------------------------------------------------------------------------- + typedef Two_d_destructive_iterator destructive_iterator; + + typedef std::reverse_iterator reverse_ne_iterator; + typedef std::reverse_iterator const_reverse_ne_iterator; + + + // Iterator functions + // ------------------ + iterator begin() { return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator cbegin() const { return const_iterator(this, 0); } + iterator end() { return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + const_iterator cend() const { return const_iterator(this, size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); } + const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); } + const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); } + + // Versions for our special non-empty iterator + // ------------------------------------------ + ne_iterator ne_begin() { return ne_iterator (_first_group); } + const_ne_iterator ne_begin() const { return const_ne_iterator(_first_group); } + const_ne_iterator ne_cbegin() const { return const_ne_iterator(_first_group); } + ne_iterator ne_end() { return ne_iterator (_last_group); } + const_ne_iterator ne_end() const { return const_ne_iterator(_last_group); } + const_ne_iterator ne_cend() const { return const_ne_iterator(_last_group); } + + reverse_ne_iterator ne_rbegin() { return reverse_ne_iterator(ne_end()); } + const_reverse_ne_iterator ne_rbegin() const { return const_reverse_ne_iterator(ne_end()); } + const_reverse_ne_iterator ne_crbegin() const { return const_reverse_ne_iterator(ne_end()); } + reverse_ne_iterator ne_rend() { return reverse_ne_iterator(ne_begin()); } + const_reverse_ne_iterator ne_rend() const { return const_reverse_ne_iterator(ne_begin()); } + const_reverse_ne_iterator ne_crend() const { return const_reverse_ne_iterator(ne_begin()); } + + destructive_iterator destructive_begin() + { + return destructive_iterator(_alloc, _first_group); + } + + destructive_iterator destructive_end() + { + return destructive_iterator(_alloc, _last_group); + } + + // How to deal with the proper group + static group_size_type num_groups(size_type num) + { + // how many to hold num buckets + return num == 0 ? (group_size_type)0 : + (group_size_type)(((num-1) / SPP_GROUP_SIZE) + 1); + } + + typename group_type::size_type pos_in_group(size_type i) const + { + return static_cast(i & SPP_MASK_); + } + + size_type group_num(size_type i) const + { + return (size_type)(i >> SPP_SHIFT_); + } + + GroupsReference which_group(size_type i) + { + return _first_group[group_num(i)]; + } + + GroupsConstReference which_group(size_type i) const + { + return _first_group[group_num(i)]; + } + + void _alloc_group_array(group_size_type sz, group_type *&first, group_type *&last) + { + if (sz) + { + first = _group_alloc.allocate((size_type)(sz + 1)); // + 1 for end marker + first[sz].mark(); // for the ne_iterator + last = first + sz; + } + } + + void _free_group_array(group_type *&first, group_type *&last) + { + if (first) + { + _group_alloc.deallocate(first, (group_size_type)(last - first + 1)); // + 1 for end marker + first = last = 0; + } + } + + void _allocate_groups(size_type sz) + { + if (sz) + { + _alloc_group_array(sz, _first_group, _last_group); + std::uninitialized_fill(_first_group, _last_group, group_type()); + } + } + + void _free_groups() + { + if (_first_group) + { + for (group_type *g = _first_group; g != _last_group; ++g) + g->destruct(_alloc); + _free_group_array(_first_group, _last_group); + } + } + + void _cleanup() + { + _free_groups(); // sets _first_group = _last_group = 0 + _table_size = 0; + _num_buckets = 0; + } + + void _init() + { + _first_group = 0; + _last_group = 0; + _table_size = 0; + _num_buckets = 0; + } + + void _copy(const sparsetable &o) + { + _table_size = o._table_size; + _num_buckets = o._num_buckets; + _alloc = o._alloc; // todo - copy or move allocator according to... + _group_alloc = o._group_alloc; // http://en.cppreference.com/w/cpp/container/unordered_map/unordered_map + + group_size_type sz = (group_size_type)(o._last_group - o._first_group); + if (sz) + { + _alloc_group_array(sz, _first_group, _last_group); + for (group_size_type i=0; iswap(o); + } + + sparsetable(sparsetable&& o, const allocator_type &alloc) + { + _init(); + this->swap(o); + _alloc = alloc; // [gp todo] is this correct? + } + + sparsetable& operator=(sparsetable&& o) + { + _cleanup(); + this->swap(o); + return *this; + } +#endif + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsetable& o) + { + using std::swap; + + swap(_first_group, o._first_group); + swap(_last_group, o._last_group); + swap(_table_size, o._table_size); + swap(_num_buckets, o._num_buckets); + if (_alloc != o._alloc) + swap(_alloc, o._alloc); + if (_group_alloc != o._group_alloc) + swap(_group_alloc, o._group_alloc); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() + { + _free_groups(); + _num_buckets = 0; + _table_size = 0; + } + + inline allocator_type get_allocator() const + { + return _alloc; + } + + + // Functions that tell you about size. + // NOTE: empty() is non-intuitive! It does not tell you the number + // of not-empty buckets (use num_nonempty() for that). Instead + // it says whether you've allocated any buckets or not. + // ---------------------------------------------------------------- + size_type size() const { return _table_size; } + size_type max_size() const { return _alloc.max_size(); } + bool empty() const { return _table_size == 0; } + size_type num_nonempty() const { return _num_buckets; } + + // OK, we'll let you resize one of these puppies + void resize(size_type new_size) + { + group_size_type sz = num_groups(new_size); + group_size_type old_sz = (group_size_type)(_last_group - _first_group); + + if (sz != old_sz) + { + // resize group array + // ------------------ + group_type *first = 0, *last = 0; + if (sz) + { + _alloc_group_array(sz, first, last); + if (old_sz) + memcpy(static_cast(first), _first_group, sizeof(*first) * (std::min)(sz, old_sz)); + } + + if (sz < old_sz) + { + for (group_type *g = _first_group + sz; g != _last_group; ++g) + g->destruct(_alloc); + } + else + std::uninitialized_fill(first + old_sz, last, group_type()); + + _free_group_array(_first_group, _last_group); + _first_group = first; + _last_group = last; + } +#if 0 + // used only in test program + // todo: fix if sparsetable to be used directly + // -------------------------------------------- + if (new_size < _table_size) + { + // lower num_buckets, clear last group + if (pos_in_group(new_size) > 0) // need to clear inside last group + groups.back().erase(_alloc, groups.back().begin() + pos_in_group(new_size), + groups.back().end()); + _num_buckets = 0; // refigure # of used buckets + for (const group_type *group = _first_group; group != _last_group; ++group) + _num_buckets += group->num_nonempty(); + } +#endif + _table_size = new_size; + } + + // We let you see if a bucket is non-empty without retrieving it + // ------------------------------------------------------------- + bool test(size_type i) const + { + // assert(i < _table_size); + return which_group(i).test(pos_in_group(i)); + } + + // also tests for erased values + // ---------------------------- + bool test_strict(size_type i) const + { + // assert(i < _table_size); + return which_group(i).test_strict(pos_in_group(i)); + } + + friend struct GrpPos; + + struct GrpPos + { + typedef typename sparsetable::ne_iterator ne_iter; + GrpPos(const sparsetable &table, size_type i) : + grp(table.which_group(i)), pos(table.pos_in_group(i)) {} + + bool test_strict() const { return grp.test_strict(pos); } + bool test() const { return grp.test(pos); } + typename sparsetable::reference unsafe_get() const { return grp.unsafe_get(pos); } + ne_iter get_iter(typename sparsetable::reference ref) + { + return ne_iter((group_type *)&grp, &ref); + } + + void erase(sparsetable &table) // item *must* be present + { + assert(table._num_buckets); + ((group_type &)grp).erase(table._alloc, pos); + --table._num_buckets; + } + + private: + GrpPos* operator=(const GrpPos&); + + const group_type &grp; + typename group_type::size_type pos; + }; + + bool test(iterator pos) const + { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + + bool test(const_iterator pos) const + { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + + // TODO(csilvers): make protected + friend + // This is used by sparse_hashtable to get an element from the table + // when we know it exists (because the caller has called test(i)). + // ----------------------------------------------------------------- + reference unsafe_get(size_type i) const + { + assert(i < _table_size); + // assert(test(i)); + return which_group(i).unsafe_get(pos_in_group(i)); + } + + // Needed for hashtables, gets as a ne_iterator. Crashes for empty bcks + const_ne_iterator get_iter(size_type i) const + { + //assert(test(i)); // how can a ne_iterator point to an empty bucket? + + size_type grp_idx = group_num(i); + + return const_ne_iterator(_first_group + grp_idx, + (_first_group[grp_idx].ne_begin() + + _first_group[grp_idx].pos_to_offset(pos_in_group(i)))); + } + + const_ne_iterator get_iter(size_type i, ColIterator col_it) const + { + return const_ne_iterator(_first_group + group_num(i), col_it); + } + + // For nonempty we can return a non-const version + ne_iterator get_iter(size_type i) + { + //assert(test(i)); // how can a nonempty_iterator point to an empty bucket? + + size_type grp_idx = group_num(i); + + return ne_iterator(_first_group + grp_idx, + (_first_group[grp_idx].ne_begin() + + _first_group[grp_idx].pos_to_offset(pos_in_group(i)))); + } + + ne_iterator get_iter(size_type i, ColIterator col_it) + { + return ne_iterator(_first_group + group_num(i), col_it); + } + + // And the reverse transformation. + size_type get_pos(const const_ne_iterator& it) const + { + difference_type current_row = it.row_current - _first_group; + difference_type current_col = (it.col_current - _first_group[current_row].ne_begin()); + return ((current_row * SPP_GROUP_SIZE) + + _first_group[current_row].offset_to_pos(current_col)); + } + + // Val can be reference or const_reference + // --------------------------------------- + template + reference set(size_type i, Val &val) + { + assert(i < _table_size); + group_type &group = which_group(i); + typename group_type::size_type old_numbuckets = group.num_nonempty(); + pointer p(group.set(_alloc, pos_in_group(i), val)); + _num_buckets += group.num_nonempty() - old_numbuckets; + return *p; + } + + // used in _move_from (where we can move the old value instead of copying it + void move(size_type i, reference val) + { + assert(i < _table_size); + which_group(i).set(_alloc, pos_in_group(i), val); + ++_num_buckets; + } + + // This takes the specified elements out of the table. + // -------------------------------------------------- + void erase(size_type i) + { + assert(i < _table_size); + + GroupsReference grp(which_group(i)); + typename group_type::size_type old_numbuckets = grp.num_nonempty(); + grp.erase(_alloc, pos_in_group(i)); + _num_buckets += grp.num_nonempty() - old_numbuckets; + } + + void erase(iterator pos) + { + erase(pos.pos); + } + + void erase(iterator start_it, iterator end_it) + { + // This could be more efficient, but then we'd need to figure + // out if we spanned groups or not. Doesn't seem worth it. + for (; start_it != end_it; ++start_it) + erase(start_it); + } + + const_ne_iterator erase(const_ne_iterator it) + { + ne_iterator res(it); + if (res.row_current->erase_ne(_alloc, res)) + _num_buckets--; + return res; + } + + const_ne_iterator erase(const_ne_iterator f, const_ne_iterator l) + { + size_t diff = l - f; + while (diff--) + f = erase(f); + return f; + } + + // We support reading and writing tables to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the groups and sizes. Returns true if all went ok. + +private: + // Every time the disk format changes, this should probably change too + typedef unsigned long MagicNumberType; + static const MagicNumberType MAGIC_NUMBER = 0x24687531; + + // Old versions of this code write all data in 32 bits. We need to + // support these files as well as having support for 64-bit systems. + // So we use the following encoding scheme: for values < 2^32-1, we + // store in 4 bytes in big-endian order. For values > 2^32, we + // store 0xFFFFFFF followed by 8 bytes in big-endian order. This + // causes us to mis-read old-version code that stores exactly + // 0xFFFFFFF, but I don't think that is likely to have happened for + // these particular values. + template + static bool write_32_or_64(OUTPUT* fp, IntType value) + { + if (value < 0xFFFFFFFFULL) // fits in 4 bytes + { + if (!sparsehash_internal::write_bigendian_number(fp, value, 4)) + return false; + } + else + { + if (!sparsehash_internal::write_bigendian_number(fp, 0xFFFFFFFFUL, 4)) + return false; + if (!sparsehash_internal::write_bigendian_number(fp, value, 8)) + return false; + } + return true; + } + + template + static bool read_32_or_64(INPUT* fp, IntType *value) + { + // reads into value + MagicNumberType first4 = 0; // a convenient 32-bit unsigned type + if (!sparsehash_internal::read_bigendian_number(fp, &first4, 4)) + return false; + + if (first4 < 0xFFFFFFFFULL) + { + *value = first4; + } + else + { + if (!sparsehash_internal::read_bigendian_number(fp, value, 8)) + return false; + } + return true; + } + +public: + // read/write_metadata() and read_write/nopointer_data() are DEPRECATED. + // Use serialize() and unserialize(), below, for new code. + + template + bool write_metadata(OUTPUT *fp) const + { + if (!write_32_or_64(fp, MAGIC_NUMBER)) return false; + if (!write_32_or_64(fp, _table_size)) return false; + if (!write_32_or_64(fp, _num_buckets)) return false; + + for (const group_type *group = _first_group; group != _last_group; ++group) + if (group->write_metadata(fp) == false) + return false; + return true; + } + + // Reading destroys the old table contents! Returns true if read ok. + template + bool read_metadata(INPUT *fp) + { + size_type magic_read = 0; + if (!read_32_or_64(fp, &magic_read)) return false; + if (magic_read != MAGIC_NUMBER) + { + clear(); // just to be consistent + return false; + } + + if (!read_32_or_64(fp, &_table_size)) return false; + if (!read_32_or_64(fp, &_num_buckets)) return false; + + resize(_table_size); // so the vector's sized ok + for (group_type *group = _first_group; group != _last_group; ++group) + if (group->read_metadata(_alloc, fp) == false) + return false; + return true; + } + + // This code is identical to that for SparseGroup + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means no pointers. + // However, we don't try to normalize endianness + bool write_nopointer_data(FILE *fp) const + { + for (const_ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!fwrite(&*it, sizeof(*it), 1, fp)) + return false; + return true; + } + + // When reading, we have to override the potential const-ness of *it + bool read_nopointer_data(FILE *fp) + { + for (ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp)) + return false; + return true; + } + + // INPUT and OUTPUT must be either a FILE, *or* a C++ stream + // (istream, ostream, etc) *or* a class providing + // Read(void*, size_t) and Write(const void*, size_t) + // (respectively), which writes a buffer into a stream + // (which the INPUT/OUTPUT instance presumably owns). + + typedef sparsehash_internal::pod_serializer NopointerSerializer; + + // ValueSerializer: a functor. operator()(OUTPUT*, const value_type&) + template + bool serialize(ValueSerializer serializer, OUTPUT *fp) + { + if (!write_metadata(fp)) + return false; + for (const_ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!serializer(fp, *it)) + return false; + return true; + } + + // ValueSerializer: a functor. operator()(INPUT*, value_type*) + template + bool unserialize(ValueSerializer serializer, INPUT *fp) + { + clear(); + if (!read_metadata(fp)) + return false; + for (ne_iterator it = ne_begin(); it != ne_end(); ++it) + if (!serializer(fp, &*it)) + return false; + return true; + } + + // Comparisons. Note the comparisons are pretty arbitrary: we + // compare values of the first index that isn't equal (using default + // value for empty buckets). + bool operator==(const sparsetable& x) const + { + return (_table_size == x._table_size && + _num_buckets == x._num_buckets && + _first_group == x._first_group); + } + + bool operator<(const sparsetable& x) const + { + return std::lexicographical_compare(begin(), end(), x.begin(), x.end()); + } + bool operator!=(const sparsetable& x) const { return !(*this == x); } + bool operator<=(const sparsetable& x) const { return !(x < *this); } + bool operator>(const sparsetable& x) const { return x < *this; } + bool operator>=(const sparsetable& x) const { return !(*this < x); } + + +private: + // The actual data + // --------------- + group_type * _first_group; + group_type * _last_group; + size_type _table_size; // how many buckets they want + size_type _num_buckets; // number of non-empty buckets + group_alloc_type _group_alloc; + allocator_type _alloc; +}; + +// ---------------------------------------------------------------------- +// S P A R S E _ H A S H T A B L E +// ---------------------------------------------------------------------- +// Hashtable class, used to implement the hashed associative containers +// hash_set and hash_map. +// +// Value: what is stored in the table (each bucket is a Value). +// Key: something in a 1-to-1 correspondence to a Value, that can be used +// to search for a Value in the table (find() takes a Key). +// HashFcn: Takes a Key and returns an integer, the more unique the better. +// ExtractKey: given a Value, returns the unique Key associated with it. +// Must inherit from unary_function, or at least have a +// result_type enum indicating the return type of operator(). +// EqualKey: Given two Keys, says whether they are the same (that is, +// if they are both associated with the same Value). +// Alloc: STL allocator to use to allocate memory. +// +// ---------------------------------------------------------------------- + +// The probing method +// ------------------ +// Linear probing +// #define JUMP_(key, num_probes) ( 1 ) +// Quadratic probing +#define JUMP_(key, num_probes) ( num_probes ) + + +// ------------------------------------------------------------------- +// ------------------------------------------------------------------- +template +class sparse_hashtable +{ +public: + typedef Key key_type; + typedef Value value_type; + typedef HashFcn hasher; // user provided or spp_hash + typedef EqualKey key_equal; + typedef Alloc allocator_type; + + typedef typename allocator_type::size_type size_type; + typedef typename allocator_type::difference_type difference_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + // Table is the main storage class. + typedef sparsetable Table; + typedef typename Table::ne_iterator ne_it; + typedef typename Table::const_ne_iterator cne_it; + typedef typename Table::destructive_iterator dest_it; + typedef typename Table::ColIterator ColIterator; + + typedef ne_it iterator; + typedef cne_it const_iterator; + typedef dest_it destructive_iterator; + + // These come from tr1. For us they're the same as regular iterators. + // ------------------------------------------------------------------- + typedef iterator local_iterator; + typedef const_iterator const_local_iterator; + + // How full we let the table get before we resize + // ---------------------------------------------- + static const int HT_OCCUPANCY_PCT; // = 80 (out of 100); + + // How empty we let the table get before we resize lower, by default. + // (0.0 means never resize lower.) + // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing + // ------------------------------------------------------------------ + static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT; + + // Minimum size we're willing to let hashtables be. + // Must be a power of two, and at least 4. + // Note, however, that for a given hashtable, the initial size is a + // function of the first constructor arg, and may be >HT_MIN_BUCKETS. + // ------------------------------------------------------------------ + static const size_type HT_MIN_BUCKETS = 4; + + // By default, if you don't specify a hashtable size at + // construction-time, we use this size. Must be a power of two, and + // at least HT_MIN_BUCKETS. + // ----------------------------------------------------------------- + static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; + + // iterators + // --------- + iterator begin() { return _mk_iterator(table.ne_begin()); } + iterator end() { return _mk_iterator(table.ne_end()); } + const_iterator begin() const { return _mk_const_iterator(table.ne_cbegin()); } + const_iterator end() const { return _mk_const_iterator(table.ne_cend()); } + const_iterator cbegin() const { return _mk_const_iterator(table.ne_cbegin()); } + const_iterator cend() const { return _mk_const_iterator(table.ne_cend()); } + + // These come from tr1 unordered_map. They iterate over 'bucket' n. + // For sparsehashtable, we could consider each 'group' to be a bucket, + // I guess, but I don't really see the point. We'll just consider + // bucket n to be the n-th element of the sparsetable, if it's occupied, + // or some empty element, otherwise. + // --------------------------------------------------------------------- + local_iterator begin(size_type i) + { + return _mk_iterator(table.test(i) ? table.get_iter(i) : table.ne_end()); + } + + local_iterator end(size_type i) + { + local_iterator it = begin(i); + if (table.test(i)) + ++it; + return _mk_iterator(it); + } + + const_local_iterator begin(size_type i) const + { + return _mk_const_iterator(table.test(i) ? table.get_iter(i) : table.ne_cend()); + } + + const_local_iterator end(size_type i) const + { + const_local_iterator it = begin(i); + if (table.test(i)) + ++it; + return _mk_const_iterator(it); + } + + const_local_iterator cbegin(size_type i) const { return begin(i); } + const_local_iterator cend(size_type i) const { return end(i); } + + // This is used when resizing + // -------------------------- + destructive_iterator destructive_begin() { return _mk_destructive_iterator(table.destructive_begin()); } + destructive_iterator destructive_end() { return _mk_destructive_iterator(table.destructive_end()); } + + + // accessor functions for the things we templatize on, basically + // ------------------------------------------------------------- + hasher hash_funct() const { return settings; } + key_equal key_eq() const { return key_info; } + allocator_type get_allocator() const { return table.get_allocator(); } + + // Accessor function for statistics gathering. + unsigned int num_table_copies() const { return settings.num_ht_copies(); } + +private: + // This is used as a tag for the copy constructor, saying to destroy its + // arg We have two ways of destructively copying: with potentially growing + // the hashtable as we copy, and without. To make sure the outside world + // can't do a destructive copy, we make the typename private. + // ----------------------------------------------------------------------- + enum MoveDontCopyT {MoveDontCopy, MoveDontGrow}; + + // creating iterators from sparsetable::ne_iterators + // ------------------------------------------------- + iterator _mk_iterator(ne_it it) const { return it; } + const_iterator _mk_const_iterator(cne_it it) const { return it; } + destructive_iterator _mk_destructive_iterator(dest_it it) const { return it; } + +public: + size_type size() const { return table.num_nonempty(); } + size_type max_size() const { return table.max_size(); } + bool empty() const { return size() == 0; } + size_type bucket_count() const { return table.size(); } + size_type max_bucket_count() const { return max_size(); } + // These are tr1 methods. Their idea of 'bucket' doesn't map well to + // what we do. We just say every bucket has 0 or 1 items in it. + size_type bucket_size(size_type i) const + { + return (size_type)(begin(i) == end(i) ? 0 : 1); + } + +private: + // Because of the above, size_type(-1) is never legal; use it for errors + // --------------------------------------------------------------------- + static const size_type ILLEGAL_BUCKET = size_type(-1); + + // Used after a string of deletes. Returns true if we actually shrunk. + // TODO(csilvers): take a delta so we can take into account inserts + // done after shrinking. Maybe make part of the Settings class? + // -------------------------------------------------------------------- + bool _maybe_shrink() + { + assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two + assert(bucket_count() >= HT_MIN_BUCKETS); + bool retval = false; + + // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, + // we'll never shrink until you get relatively big, and we'll never + // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something + // like "dense_hash_set x; x.insert(4); x.erase(4);" will + // shrink us down to HT_MIN_BUCKETS buckets, which is too small. + // --------------------------------------------------------------- + const size_type num_remain = table.num_nonempty(); + const size_type shrink_threshold = settings.shrink_threshold(); + if (shrink_threshold > 0 && num_remain < shrink_threshold && + bucket_count() > HT_DEFAULT_STARTING_BUCKETS) + { + const float shrink_factor = settings.shrink_factor(); + size_type sz = (size_type)(bucket_count() / 2); // find how much we should shrink + while (sz > HT_DEFAULT_STARTING_BUCKETS && + num_remain < static_cast(sz * shrink_factor)) + { + sz /= 2; // stay a power of 2 + } + sparse_hashtable tmp(MoveDontCopy, *this, sz); + swap(tmp); // now we are tmp + retval = true; + } + settings.set_consider_shrink(false); // because we just considered it + return retval; + } + + // We'll let you resize a hashtable -- though this makes us copy all! + // When you resize, you say, "make it big enough for this many more elements" + // Returns true if we actually resized, false if size was already ok. + // -------------------------------------------------------------------------- + bool _resize_delta(size_type delta) + { + bool did_resize = false; + if (settings.consider_shrink()) + { + // see if lots of deletes happened + if (_maybe_shrink()) + did_resize = true; + } + if (table.num_nonempty() >= + (std::numeric_limits::max)() - delta) + { + throw_exception(std::length_error("resize overflow")); + } + + size_type num_occupied = (size_type)(table.num_nonempty() + num_deleted); + + if (bucket_count() >= HT_MIN_BUCKETS && + (num_occupied + delta) <= settings.enlarge_threshold()) + return did_resize; // we're ok as we are + + // Sometimes, we need to resize just to get rid of all the + // "deleted" buckets that are clogging up the hashtable. So when + // deciding whether to resize, count the deleted buckets (which + // are currently taking up room). + // ------------------------------------------------------------- + const size_type needed_size = + settings.min_buckets((size_type)(num_occupied + delta), (size_type)0); + + if (needed_size <= bucket_count()) // we have enough buckets + return did_resize; + + size_type resize_to = settings.min_buckets((size_type)(num_occupied + delta), bucket_count()); + + if (resize_to < needed_size && // may double resize_to + resize_to < (std::numeric_limits::max)() / 2) + { + // This situation means that we have enough deleted elements, + // that once we purge them, we won't actually have needed to + // grow. But we may want to grow anyway: if we just purge one + // element, say, we'll have to grow anyway next time we + // insert. Might as well grow now, since we're already going + // through the trouble of copying (in order to purge the + // deleted elements). + const size_type target = + static_cast(settings.shrink_size((size_type)(resize_to*2))); + if (table.num_nonempty() + delta >= target) + { + // Good, we won't be below the shrink threshhold even if we double. + resize_to *= 2; + } + } + + sparse_hashtable tmp(MoveDontCopy, *this, resize_to); + swap(tmp); // now we are tmp + return true; + } + + // Used to actually do the rehashing when we grow/shrink a hashtable + // ----------------------------------------------------------------- + void _copy_from(const sparse_hashtable &ht, size_type min_buckets_wanted) + { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + const size_type resize_to = settings.min_buckets(ht.size(), min_buckets_wanted); + + if (resize_to > bucket_count()) + { + // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + settings.reset_thresholds(bucket_count()); + } + + // We use a normal iterator to get bcks from ht + // We could use insert() here, but since we know there are + // no duplicates, we can be more efficient + assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two + for (const_iterator it = ht.begin(); it != ht.end(); ++it) + { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + const size_type bucket_count_minus_one = bucket_count() - 1; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + table.test(bucknum); // table.test() OK since no erase() + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) + { + ++num_probes; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + table.set(bucknum, *it); // copies the value to here + } + settings.inc_num_ht_copies(); + } + + // Implementation is like _copy_from, but it destroys the table of the + // "from" guy by freeing sparsetable memory as we iterate. This is + // useful in resizing, since we're throwing away the "from" guy anyway. + // -------------------------------------------------------------------- + void _move_from(MoveDontCopyT mover, sparse_hashtable &ht, + size_type min_buckets_wanted) + { + clear(); + + // If we need to change the size of our table, do it now + size_type resize_to; + if (mover == MoveDontGrow) + resize_to = ht.bucket_count(); // keep same size as old ht + else // MoveDontCopy + resize_to = settings.min_buckets(ht.size(), min_buckets_wanted); + if (resize_to > bucket_count()) + { + // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + settings.reset_thresholds(bucket_count()); + } + + // We use a normal iterator to get bcks from ht + // We could use insert() here, but since we know there are + // no duplicates, we can be more efficient + assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two + const size_type bucket_count_minus_one = (const size_type)(bucket_count() - 1); + + // THIS IS THE MAJOR LINE THAT DIFFERS FROM COPY_FROM(): + for (destructive_iterator it = ht.destructive_begin(); + it != ht.destructive_end(); ++it) + { + size_type num_probes = 0; + size_type bucknum; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + table.test(bucknum); // table.test() OK since no erase() + bucknum = (size_type)((bucknum + JUMP_(key, num_probes)) & (bucket_count()-1))) + { + ++num_probes; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + table.move(bucknum, *it); // moves the value to here + } + settings.inc_num_ht_copies(); + } + + + // Required by the spec for hashed associative container +public: + // Though the docs say this should be num_buckets, I think it's much + // more useful as num_elements. As a special feature, calling with + // req_elements==0 will cause us to shrink if we can, saving space. + // ----------------------------------------------------------------- + void resize(size_type req_elements) + { + // resize to this or larger + if (settings.consider_shrink() || req_elements == 0) + _maybe_shrink(); + if (req_elements > table.num_nonempty()) // we only grow + _resize_delta((size_type)(req_elements - table.num_nonempty())); + } + + // Get and change the value of shrink_factor and enlarge_factor. The + // description at the beginning of this file explains how to choose + // the values. Setting the shrink parameter to 0.0 ensures that the + // table never shrinks. + // ------------------------------------------------------------------ + void get_resizing_parameters(float* shrink, float* grow) const + { + *shrink = settings.shrink_factor(); + *grow = settings.enlarge_factor(); + } + + float get_shrink_factor() const { return settings.shrink_factor(); } + float get_enlarge_factor() const { return settings.enlarge_factor(); } + + void set_resizing_parameters(float shrink, float grow) + { + settings.set_resizing_parameters(shrink, grow); + settings.reset_thresholds(bucket_count()); + } + + void set_shrink_factor(float shrink) + { + set_resizing_parameters(shrink, get_enlarge_factor()); + } + + void set_enlarge_factor(float grow) + { + set_resizing_parameters(get_shrink_factor(), grow); + } + + // CONSTRUCTORS -- as required by the specs, we take a size, + // but also let you specify a hashfunction, key comparator, + // and key extractor. We also define a copy constructor and =. + // DESTRUCTOR -- the default is fine, surprisingly. + // ------------------------------------------------------------ + explicit sparse_hashtable(size_type expected_max_items_in_table = 0, + const HashFcn& hf = HashFcn(), + const EqualKey& eql = EqualKey(), + const ExtractKey& ext = ExtractKey(), + const SetKey& set = SetKey(), + const allocator_type& alloc = allocator_type()) + : settings(hf), + key_info(ext, set, eql), + num_deleted(0), + table((expected_max_items_in_table == 0 + ? HT_DEFAULT_STARTING_BUCKETS + : settings.min_buckets(expected_max_items_in_table, 0)), + alloc) + { + settings.reset_thresholds(bucket_count()); + } + + // As a convenience for resize(), we allow an optional second argument + // which lets you make this new hashtable a different size than ht. + // We also provide a mechanism of saying you want to "move" the ht argument + // into us instead of copying. + // ------------------------------------------------------------------------ + sparse_hashtable(const sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : settings(ht.settings), + key_info(ht.key_info), + num_deleted(0), + table(0) + { + settings.reset_thresholds(bucket_count()); + _copy_from(ht, min_buckets_wanted); + } + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + + sparse_hashtable(sparse_hashtable&& o, const allocator_type& alloc = allocator_type()) : + settings(o.settings), + key_info(o.key_info), + num_deleted(0), + table(HT_DEFAULT_STARTING_BUCKETS, alloc) + { + settings.reset_thresholds(bucket_count()); + this->swap(o); + } + + sparse_hashtable& operator=(sparse_hashtable&& o) + { + this->swap(o); + return *this; + } +#endif + + sparse_hashtable(MoveDontCopyT mover, + sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : settings(ht.settings), + key_info(ht.key_info), + num_deleted(0), + table(min_buckets_wanted, ht.table.get_allocator()) + //table(min_buckets_wanted) + { + settings.reset_thresholds(bucket_count()); + _move_from(mover, ht, min_buckets_wanted); + } + + sparse_hashtable& operator=(const sparse_hashtable& ht) + { + if (&ht == this) + return *this; // don't copy onto ourselves + settings = ht.settings; + key_info = ht.key_info; + num_deleted = ht.num_deleted; + + // _copy_from() calls clear and sets num_deleted to 0 too + _copy_from(ht, HT_MIN_BUCKETS); + + // we purposefully don't copy the allocator, which may not be copyable + return *this; + } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparse_hashtable& ht) + { + using std::swap; + + swap(settings, ht.settings); + swap(key_info, ht.key_info); + swap(num_deleted, ht.num_deleted); + table.swap(ht.table); + settings.reset_thresholds(bucket_count()); // also resets consider_shrink + ht.settings.reset_thresholds(ht.bucket_count()); + // we purposefully don't swap the allocator, which may not be swap-able + } + + // It's always nice to be able to clear a table without deallocating it + void clear() + { + if (!empty() || num_deleted != 0) + { + table.clear(); + table = Table(HT_DEFAULT_STARTING_BUCKETS, table.get_allocator()); + } + settings.reset_thresholds(bucket_count()); + num_deleted = 0; + } + + // LOOKUP ROUTINES +private: + + enum pos_type { pt_empty = 0, pt_erased, pt_full }; + // ------------------------------------------------------------------- + class Position + { + public: + + Position() : _t(pt_empty) {} + Position(pos_type t, size_type idx) : _t(t), _idx(idx) {} + + pos_type _t; + size_type _idx; + }; + + // Returns a pair: + // - 'first' is a code, 2 if key already present, 0 or 1 otherwise. + // - 'second' is a position, where the key should go + // Note: because of deletions where-to-insert is not trivial: it's the + // first deleted bucket we see, as long as we don't find the key later + // ------------------------------------------------------------------- + Position _find_position(const key_type &key) const + { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = (const size_type)(bucket_count() - 1); + size_type bucknum = hash(key) & bucket_count_minus_one; + Position pos; + + while (1) + { + // probe until something happens + // ----------------------------- + typename Table::GrpPos grp_pos(table, bucknum); + + if (!grp_pos.test_strict()) + { + // bucket is empty => key not present + return pos._t ? pos : Position(pt_empty, bucknum); + } + else if (grp_pos.test()) + { + reference ref(grp_pos.unsafe_get()); + + if (equals(key, get_key(ref))) + return Position(pt_full, bucknum); + } + else if (pos._t == pt_empty) + { + // first erased position + pos._t = pt_erased; + pos._idx = bucknum; + } + + ++num_probes; // we're doing another probe + bucknum = (size_type)((bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one); + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + +public: + // I hate to duplicate find() like that, but it is + // significantly faster to not have the intermediate pair + // ------------------------------------------------------------------ + iterator find(const key_type& key) + { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + + while (1) // probe until something happens + { + typename Table::GrpPos grp_pos(table, bucknum); + + if (!grp_pos.test_strict()) + return end(); // bucket is empty + if (grp_pos.test()) + { + reference ref(grp_pos.unsafe_get()); + + if (equals(key, get_key(ref))) + return grp_pos.get_iter(ref); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + // Wish I could avoid the duplicate find() const and non-const. + // ------------------------------------------------------------ + const_iterator find(const key_type& key) const + { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + + while (1) // probe until something happens + { + typename Table::GrpPos grp_pos(table, bucknum); + + if (!grp_pos.test_strict()) + return end(); // bucket is empty + else if (grp_pos.test()) + { + reference ref(grp_pos.unsafe_get()); + + if (equals(key, get_key(ref))) + return _mk_const_iterator(table.get_iter(bucknum, &ref)); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + // This is a tr1 method: the bucket a given key is in, or what bucket + // it would be put in, if it were to be inserted. Shrug. + // ------------------------------------------------------------------ + size_type bucket(const key_type& key) const + { + Position pos = _find_position(key); + return pos._idx; + } + + // Counts how many elements have key key. For maps, it's either 0 or 1. + // --------------------------------------------------------------------- + size_type count(const key_type &key) const + { + Position pos = _find_position(key); + return (size_type)(pos._t == pt_full ? 1 : 0); + } + + // Likewise, equal_range doesn't really make sense for us. Oh well. + // ----------------------------------------------------------------- + std::pair equal_range(const key_type& key) + { + iterator pos = find(key); // either an iterator or end + if (pos == end()) + return std::pair(pos, pos); + else + { + const iterator startpos = pos++; + return std::pair(startpos, pos); + } + } + + std::pair equal_range(const key_type& key) const + { + const_iterator pos = find(key); // either an iterator or end + if (pos == end()) + return std::pair(pos, pos); + else + { + const const_iterator startpos = pos++; + return std::pair(startpos, pos); + } + } + + + // INSERTION ROUTINES +private: + // Private method used by insert_noresize and find_or_insert. + template + reference _insert_at(T& obj, size_type pos, bool erased) + { + if (size() >= max_size()) + { + throw_exception(std::length_error("insert overflow")); + } + if (erased) + { + assert(num_deleted); + --num_deleted; + } + return table.set(pos, obj); + } + + // If you know *this is big enough to hold obj, use this routine + template + std::pair _insert_noresize(T& obj) + { + Position pos = _find_position(get_key(obj)); + bool already_there = (pos._t == pt_full); + + if (!already_there) + { + reference ref(_insert_at(obj, pos._idx, pos._t == pt_erased)); + return std::pair(_mk_iterator(table.get_iter(pos._idx, &ref)), true); + } + return std::pair(_mk_iterator(table.get_iter(pos._idx)), false); + } + + // Specializations of insert(it, it) depending on the power of the iterator: + // (1) Iterator supports operator-, resize before inserting + template + void _insert(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag /*unused*/) + { + int64_t dist = std::distance(f, l); + if (dist < 0 || static_cast(dist) >= (std::numeric_limits::max)()) + throw_exception(std::length_error("insert-range overflow")); + + _resize_delta(static_cast(dist)); + + for (; dist > 0; --dist, ++f) + _insert_noresize(*f); + } + + // (2) Arbitrary iterator, can't tell how much to resize + template + void _insert(InputIterator f, InputIterator l, std::input_iterator_tag /*unused*/) + { + for (; f != l; ++f) + _insert(*f); + } + +public: + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + std::pair emplace(Args&&... args) + { + _resize_delta(1); + value_type obj(std::forward(args)...); + return _insert_noresize(obj); + } +#endif + + // This is the normal insert routine, used by the outside world + std::pair insert(const_reference obj) + { + _resize_delta(1); // adding an object, grow if need be + return _insert_noresize(obj); + } + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + template< class P > + std::pair insert(P &&obj) + { + _resize_delta(1); // adding an object, grow if need be + value_type val(std::forward

(obj)); + return _insert_noresize(val); + } +#endif + + // When inserting a lot at a time, we specialize on the type of iterator + template + void insert(InputIterator f, InputIterator l) + { + // specializes on iterator type + _insert(f, l, + typename std::iterator_traits::iterator_category()); + } + + // DefaultValue is a functor that takes a key and returns a value_type + // representing the default value to be inserted if none is found. +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + value_type& find_or_insert(KT&& key) +#else + template + value_type& find_or_insert(const key_type& key) +#endif + { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + DefaultValue default_value; + size_type erased_pos = 0; + bool erased = false; + + while (1) // probe until something happens + { + typename Table::GrpPos grp_pos(table, bucknum); + + if (!grp_pos.test_strict()) + { + // not found +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + auto&& def(default_value(std::forward(key))); +#else + value_type def(default_value(key)); +#endif + if (_resize_delta(1)) + { + // needed to rehash to make room + // Since we resized, we can't use pos, so recalculate where to insert. + return *(_insert_noresize(def).first); + } + else + { + // no need to rehash, insert right here + return _insert_at(def, erased ? erased_pos : bucknum, erased); + } + } + if (grp_pos.test()) + { + reference ref(grp_pos.unsafe_get()); + + if (equals(key, get_key(ref))) + return ref; + } + else if (!erased) + { + // first erased position + erased_pos = bucknum; + erased = true; + } + + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + size_type erase(const key_type& key) + { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + + while (1) // probe until something happens + { + typename Table::GrpPos grp_pos(table, bucknum); + + if (!grp_pos.test_strict()) + return 0; // bucket is empty, we deleted nothing + if (grp_pos.test()) + { + reference ref(grp_pos.unsafe_get()); + + if (equals(key, get_key(ref))) + { + grp_pos.erase(table); + ++num_deleted; + settings.set_consider_shrink(true); // will think about shrink after next insert + return 1; // because we deleted one thing + } + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + const_iterator erase(const_iterator pos) + { + if (pos == cend()) + return cend(); // sanity check + + const_iterator nextpos = table.erase(pos); + ++num_deleted; + settings.set_consider_shrink(true); + return nextpos; + } + + const_iterator erase(const_iterator f, const_iterator l) + { + if (f == cend()) + return cend(); // sanity check + + size_type num_before = table.num_nonempty(); + const_iterator nextpos = table.erase(f, l); + num_deleted += num_before - table.num_nonempty(); + settings.set_consider_shrink(true); + return nextpos; + } + + // Deleted key routines - just to keep google test framework happy + // we don't actually use the deleted key + // --------------------------------------------------------------- + void set_deleted_key(const key_type&) + { + } + + void clear_deleted_key() + { + } + + bool operator==(const sparse_hashtable& ht) const + { + if (this == &ht) + return true; + + if (size() != ht.size()) + return false; + + for (const_iterator it = begin(); it != end(); ++it) + { + const_iterator it2 = ht.find(get_key(*it)); + if ((it2 == ht.end()) || (*it != *it2)) + return false; + } + + return true; + } + + bool operator!=(const sparse_hashtable& ht) const + { + return !(*this == ht); + } + + + // I/O + // We support reading and writing hashtables to disk. NOTE that + // this only stores the hashtable metadata, not the stuff you've + // actually put in the hashtable! Alas, since I don't know how to + // write a hasher or key_equal, you have to make sure everything + // but the table is the same. We compact before writing. + // + // The OUTPUT type needs to support a Write() operation. File and + // OutputBuffer are appropriate types to pass in. + // + // The INPUT type needs to support a Read() operation. File and + // InputBuffer are appropriate types to pass in. + // ------------------------------------------------------------- + template + bool write_metadata(OUTPUT *fp) + { + return table.write_metadata(fp); + } + + template + bool read_metadata(INPUT *fp) + { + num_deleted = 0; // since we got rid before writing + const bool result = table.read_metadata(fp); + settings.reset_thresholds(bucket_count()); + return result; + } + + // Only meaningful if value_type is a POD. + template + bool write_nopointer_data(OUTPUT *fp) + { + return table.write_nopointer_data(fp); + } + + // Only meaningful if value_type is a POD. + template + bool read_nopointer_data(INPUT *fp) + { + return table.read_nopointer_data(fp); + } + + // INPUT and OUTPUT must be either a FILE, *or* a C++ stream + // (istream, ostream, etc) *or* a class providing + // Read(void*, size_t) and Write(const void*, size_t) + // (respectively), which writes a buffer into a stream + // (which the INPUT/OUTPUT instance presumably owns). + + typedef sparsehash_internal::pod_serializer NopointerSerializer; + + // ValueSerializer: a functor. operator()(OUTPUT*, const value_type&) + template + bool serialize(ValueSerializer serializer, OUTPUT *fp) + { + return table.serialize(serializer, fp); + } + + // ValueSerializer: a functor. operator()(INPUT*, value_type*) + template + bool unserialize(ValueSerializer serializer, INPUT *fp) + { + num_deleted = 0; // since we got rid before writing + const bool result = table.unserialize(serializer, fp); + settings.reset_thresholds(bucket_count()); + return result; + } + +private: + + // Package templated functors with the other types to eliminate memory + // needed for storing these zero-size operators. Since ExtractKey and + // hasher's operator() might have the same function signature, they + // must be packaged in different classes. + // ------------------------------------------------------------------------- + struct Settings : + sparsehash_internal::sh_hashtable_settings + { + explicit Settings(const hasher& hf) + : sparsehash_internal::sh_hashtable_settings + (hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} + }; + + // KeyInfo stores delete key and packages zero-size functors: + // ExtractKey and SetKey. + // --------------------------------------------------------- + class KeyInfo : public ExtractKey, public SetKey, public EqualKey + { + public: + KeyInfo(const ExtractKey& ek, const SetKey& sk, const EqualKey& eq) + : ExtractKey(ek), SetKey(sk), EqualKey(eq) + { + } + + // We want to return the exact same type as ExtractKey: Key or const Key& + typename ExtractKey::result_type get_key(const_reference v) const + { + return ExtractKey::operator()(v); + } + + bool equals(const key_type& a, const key_type& b) const + { + return EqualKey::operator()(a, b); + } + }; + + // Utility functions to access the templated operators + size_t hash(const key_type& v) const + { + return settings.hash(v); + } + + bool equals(const key_type& a, const key_type& b) const + { + return key_info.equals(a, b); + } + + typename ExtractKey::result_type get_key(const_reference v) const + { + return key_info.get_key(v); + } + +private: + // Actual data + // ----------- + Settings settings; + KeyInfo key_info; + size_type num_deleted; + Table table; // holds num_buckets and num_elements too +}; + +#undef JUMP_ + +// ----------------------------------------------------------------------------- +template +const typename sparse_hashtable::size_type +sparse_hashtable::ILLEGAL_BUCKET; + +// How full we let the table get before we resize. Knuth says .8 is +// good -- higher causes us to probe too much, though saves memory +// ----------------------------------------------------------------------------- +template +const int sparse_hashtable::HT_OCCUPANCY_PCT = 50; + +// How empty we let the table get before we resize lower. +// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing +// ----------------------------------------------------------------------------- +template +const int sparse_hashtable::HT_EMPTY_PCT += static_cast(0.4 * + sparse_hashtable::HT_OCCUPANCY_PCT); + + +// ---------------------------------------------------------------------- +// S P A R S E _ H A S H _ M A P +// ---------------------------------------------------------------------- +template , + class EqualKey = std::equal_to, + class Alloc = SPP_DEFAULT_ALLOCATOR > > +class sparse_hash_map +{ +public: + typedef typename std::pair value_type; + +private: + // Apparently select1st is not stl-standard, so we define our own + struct SelectKey + { + typedef const Key& result_type; + + inline const Key& operator()(const value_type& p) const + { + return p.first; + } + }; + + struct SetKey + { + inline void operator()(value_type* value, const Key& new_key) const + { + *const_cast(&value->first) = new_key; + } + }; + + // For operator[]. + struct DefaultValue + { +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + inline value_type operator()(KT&& key) const + { + return { std::forward(key), T() }; + } +#else + inline value_type operator()(const Key& key) const + { + return std::make_pair(key, T()); + } +#endif + }; + + // The actual data + typedef sparse_hashtable ht; + +public: + typedef typename ht::key_type key_type; + typedef T data_type; + typedef T mapped_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + // Iterator functions + iterator begin() { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator begin() const { return rep.cbegin(); } + const_iterator end() const { return rep.cend(); } + const_iterator cbegin() const { return rep.cbegin(); } + const_iterator cend() const { return rep.cend(); } + + // These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) { return rep.begin(i); } + local_iterator end(size_type i) { return rep.end(i); } + const_local_iterator begin(size_type i) const { return rep.begin(i); } + const_local_iterator end(size_type i) const { return rep.end(i); } + const_local_iterator cbegin(size_type i) const { return rep.cbegin(i); } + const_local_iterator cend(size_type i) const { return rep.cend(i); } + + // Accessor functions + // ------------------ + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + // ------------ + explicit sparse_hash_map(size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(n, hf, eql, SelectKey(), SetKey(), alloc) + { + } + + explicit sparse_hash_map(const allocator_type& alloc) : + rep(0, hasher(), key_equal(), SelectKey(), SetKey(), alloc) + { + } + + sparse_hash_map(size_type n, const allocator_type& alloc) : + rep(n, hasher(), key_equal(), SelectKey(), SetKey(), alloc) + { + } + + sparse_hash_map(size_type n, const hasher& hf, const allocator_type& alloc) : + rep(n, hf, key_equal(), SelectKey(), SetKey(), alloc) + { + } + + template + sparse_hash_map(InputIterator f, InputIterator l, + size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(n, hf, eql, SelectKey(), SetKey(), alloc) + { + rep.insert(f, l); + } + + template + sparse_hash_map(InputIterator f, InputIterator l, + size_type n, const allocator_type& alloc) + : rep(n, hasher(), key_equal(), SelectKey(), SetKey(), alloc) + { + rep.insert(f, l); + } + + template + sparse_hash_map(InputIterator f, InputIterator l, + size_type n, const hasher& hf, const allocator_type& alloc) + : rep(n, hf, key_equal(), SelectKey(), SetKey(), alloc) + { + rep.insert(f, l); + } + + sparse_hash_map(const sparse_hash_map &o) : + rep(o.rep) + {} + + sparse_hash_map(const sparse_hash_map &o, + const allocator_type& alloc) : + rep(o.rep, alloc) + {} + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + sparse_hash_map(sparse_hash_map &&o) : + rep(std::move(o.rep)) + {} + + sparse_hash_map(sparse_hash_map &&o, + const allocator_type& alloc) : + rep(std::move(o.rep), alloc) + {} + + sparse_hash_map& operator=(sparse_hash_map &&o) + { + rep = std::move(o.rep); + } +#endif + +#if !defined(SPP_NO_CXX11_HDR_INITIALIZER_LIST) + sparse_hash_map(std::initializer_list init, + size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(n, hf, eql, SelectKey(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_map(std::initializer_list init, + size_type n, const allocator_type& alloc) : + rep(n, hasher(), key_equal(), SelectKey(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_map(std::initializer_list init, + size_type n, const hasher& hf, const allocator_type& alloc) : + rep(n, hf, key_equal(), SelectKey(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_map& operator=(std::initializer_list init) + { + rep.clear(); + rep.insert(init.begin(), init.end()); + return *this; + } + + void insert(std::initializer_list init) + { + rep.insert(init.begin(), init.end()); + } +#endif + + sparse_hash_map& operator=(const sparse_hash_map &o) + { + rep = o.rep; + return *this; + } + + void clear() { rep.clear(); } + void swap(sparse_hash_map& hs) { rep.swap(hs.rep); } + + // Functions concerning size + // ------------------------- + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + float load_factor() const { return size() * 1.0f / bucket_count(); } + + float max_load_factor() const { return rep.get_enlarge_factor(); } + void max_load_factor(float grow) { rep.set_enlarge_factor(grow); } + + float min_load_factor() const { return rep.get_shrink_factor(); } + void min_load_factor(float shrink){ rep.set_shrink_factor(shrink); } + + void set_resizing_parameters(float shrink, float grow) + { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type cnt) { rep.resize(cnt); } + void rehash(size_type cnt) { resize(cnt); } // c++11 name + void reserve(size_type cnt) { resize(cnt); } // c++11 + + // Lookup + // ------ + iterator find(const key_type& key) { return rep.find(key); } + const_iterator find(const key_type& key) const { return rep.find(key); } + bool contains(const key_type& key) const { return rep.find(key) != rep.end(); } + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + mapped_type& operator[](KT&& key) + { + return rep.template find_or_insert(std::forward(key)).second; + } +#else + mapped_type& operator[](const key_type& key) + { + return rep.template find_or_insert(key).second; + } +#endif + + size_type count(const key_type& key) const { return rep.count(key); } + + std::pair + equal_range(const key_type& key) { return rep.equal_range(key); } + + std::pair + equal_range(const key_type& key) const { return rep.equal_range(key); } + + mapped_type& at(const key_type& key) + { + iterator it = rep.find(key); + if (it == rep.end()) + throw_exception(std::out_of_range("at: key not present")); + return it->second; + } + + const mapped_type& at(const key_type& key) const + { + const_iterator it = rep.find(key); + if (it == rep.cend()) + throw_exception(std::out_of_range("at: key not present")); + return it->second; + } + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + std::pair emplace(Args&&... args) + { + return rep.emplace(std::forward(args)...); + } + + template + iterator emplace_hint(const_iterator , Args&&... args) + { + return rep.emplace(std::forward(args)...).first; + } +#endif + + // Insert + // ------ + std::pair + insert(const value_type& obj) { return rep.insert(obj); } + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + template< class P > + std::pair insert(P&& obj) { return rep.insert(std::forward

(obj)); } +#endif + + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + + iterator insert(iterator /*unused*/, const value_type& obj) { return insert(obj).first; } + iterator insert(const_iterator /*unused*/, const value_type& obj) { return insert(obj).first; } + + // Deleted key routines - just to keep google test framework happy + // we don't actually use the deleted key + // --------------------------------------------------------------- + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // Erase + // ----- + size_type erase(const key_type& key) { return rep.erase(key); } + iterator erase(iterator it) { return rep.erase(it); } + iterator erase(iterator f, iterator l) { return rep.erase(f, l); } + iterator erase(const_iterator it) { return rep.erase(it); } + iterator erase(const_iterator f, const_iterator l){ return rep.erase(f, l); } + + // Comparison + // ---------- + bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + // + // For maximum flexibility, this does not assume a particular + // file type (though it will probably be a FILE *). We just pass + // the fp through to rep. + + // If your keys and values are simple enough, you can pass this + // serializer to serialize()/unserialize(). "Simple enough" means + // value_type is a POD type that contains no pointers. Note, + // however, we don't try to normalize endianness. + // --------------------------------------------------------------- + typedef typename ht::NopointerSerializer NopointerSerializer; + + // serializer: a class providing operator()(OUTPUT*, const value_type&) + // (writing value_type to OUTPUT). You can specify a + // NopointerSerializer object if appropriate (see above). + // fp: either a FILE*, OR an ostream*/subclass_of_ostream*, OR a + // pointer to a class providing size_t Write(const void*, size_t), + // which writes a buffer into a stream (which fp presumably + // owns) and returns the number of bytes successfully written. + // Note basic_ostream is not currently supported. + // --------------------------------------------------------------- + template + bool serialize(ValueSerializer serializer, OUTPUT* fp) + { + return rep.serialize(serializer, fp); + } + + // serializer: a functor providing operator()(INPUT*, value_type*) + // (reading from INPUT and into value_type). You can specify a + // NopointerSerializer object if appropriate (see above). + // fp: either a FILE*, OR an istream*/subclass_of_istream*, OR a + // pointer to a class providing size_t Read(void*, size_t), + // which reads into a buffer from a stream (which fp presumably + // owns) and returns the number of bytes successfully read. + // Note basic_istream is not currently supported. + // NOTE: Since value_type is std::pair, ValueSerializer + // may need to do a const cast in order to fill in the key. + // NOTE: if Key or T are not POD types, the serializer MUST use + // placement-new to initialize their values, rather than a normal + // equals-assignment or similar. (The value_type* passed into the + // serializer points to garbage memory.) + // --------------------------------------------------------------- + template + bool unserialize(ValueSerializer serializer, INPUT* fp) + { + return rep.unserialize(serializer, fp); + } + + // The four methods below are DEPRECATED. + // Use serialize() and unserialize() for new code. + // ----------------------------------------------- + template + bool write_metadata(OUTPUT *fp) { return rep.write_metadata(fp); } + + template + bool read_metadata(INPUT *fp) { return rep.read_metadata(fp); } + + template + bool write_nopointer_data(OUTPUT *fp) { return rep.write_nopointer_data(fp); } + + template + bool read_nopointer_data(INPUT *fp) { return rep.read_nopointer_data(fp); } + + +private: + // The actual data + // --------------- + ht rep; +}; + +// ---------------------------------------------------------------------- +// S P A R S E _ H A S H _ S E T +// ---------------------------------------------------------------------- + +template , + class EqualKey = std::equal_to, + class Alloc = SPP_DEFAULT_ALLOCATOR > +class sparse_hash_set +{ +private: + // Apparently identity is not stl-standard, so we define our own + struct Identity + { + typedef const Value& result_type; + inline const Value& operator()(const Value& v) const { return v; } + }; + + struct SetKey + { + inline void operator()(Value* value, const Value& new_key) const + { + *value = new_key; + } + }; + + typedef sparse_hashtable ht; + +public: + typedef typename ht::key_type key_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::const_pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::const_reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::const_iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::const_local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + + // Iterator functions -- recall all iterators are const + iterator begin() const { return rep.begin(); } + iterator end() const { return rep.end(); } + const_iterator cbegin() const { return rep.cbegin(); } + const_iterator cend() const { return rep.cend(); } + + // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) const { return rep.begin(i); } + local_iterator end(size_type i) const { return rep.end(i); } + local_iterator cbegin(size_type i) const { return rep.cbegin(i); } + local_iterator cend(size_type i) const { return rep.cend(i); } + + + // Accessor functions + // ------------------ + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } // tr1 name + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + // ------------ + explicit sparse_hash_set(size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) : + rep(n, hf, eql, Identity(), SetKey(), alloc) + { + } + + explicit sparse_hash_set(const allocator_type& alloc) : + rep(0, hasher(), key_equal(), Identity(), SetKey(), alloc) + { + } + + sparse_hash_set(size_type n, const allocator_type& alloc) : + rep(n, hasher(), key_equal(), Identity(), SetKey(), alloc) + { + } + + sparse_hash_set(size_type n, const hasher& hf, + const allocator_type& alloc) : + rep(n, hf, key_equal(), Identity(), SetKey(), alloc) + { + } + + template + sparse_hash_set(InputIterator f, InputIterator l, + size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(n, hf, eql, Identity(), SetKey(), alloc) + { + rep.insert(f, l); + } + + template + sparse_hash_set(InputIterator f, InputIterator l, + size_type n, const allocator_type& alloc) + : rep(n, hasher(), key_equal(), Identity(), SetKey(), alloc) + { + rep.insert(f, l); + } + + template + sparse_hash_set(InputIterator f, InputIterator l, + size_type n, const hasher& hf, const allocator_type& alloc) + : rep(n, hf, key_equal(), Identity(), SetKey(), alloc) + { + rep.insert(f, l); + } + + sparse_hash_set(const sparse_hash_set &o) : + rep(o.rep) + {} + + sparse_hash_set(const sparse_hash_set &o, + const allocator_type& alloc) : + rep(o.rep, alloc) + {} + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + sparse_hash_set(sparse_hash_set &&o) : + rep(std::move(o.rep)) + {} + + sparse_hash_set(sparse_hash_set &&o, + const allocator_type& alloc) : + rep(std::move(o.rep), alloc) + {} +#endif + +#if !defined(SPP_NO_CXX11_HDR_INITIALIZER_LIST) + sparse_hash_set(std::initializer_list init, + size_type n = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) : + rep(n, hf, eql, Identity(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_set(std::initializer_list init, + size_type n, const allocator_type& alloc) : + rep(n, hasher(), key_equal(), Identity(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_set(std::initializer_list init, + size_type n, const hasher& hf, + const allocator_type& alloc) : + rep(n, hf, key_equal(), Identity(), SetKey(), alloc) + { + rep.insert(init.begin(), init.end()); + } + + sparse_hash_set& operator=(std::initializer_list init) + { + rep.clear(); + rep.insert(init.begin(), init.end()); + return *this; + } + + void insert(std::initializer_list init) + { + rep.insert(init.begin(), init.end()); + } + +#endif + + sparse_hash_set& operator=(const sparse_hash_set &o) + { + rep = o.rep; + return *this; + } + + void clear() { rep.clear(); } + void swap(sparse_hash_set& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + // ------------------------- + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + + float load_factor() const { return size() * 1.0f / bucket_count(); } + + float max_load_factor() const { return rep.get_enlarge_factor(); } + void max_load_factor(float grow) { rep.set_enlarge_factor(grow); } + + float min_load_factor() const { return rep.get_shrink_factor(); } + void min_load_factor(float shrink){ rep.set_shrink_factor(shrink); } + + void set_resizing_parameters(float shrink, float grow) + { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type cnt) { rep.resize(cnt); } + void rehash(size_type cnt) { resize(cnt); } // c++11 name + void reserve(size_type cnt) { resize(cnt); } // c++11 + + // Lookup + // ------ + iterator find(const key_type& key) const { return rep.find(key); } + bool contains(const key_type& key) const { return rep.find(key) != rep.end(); } + + size_type count(const key_type& key) const { return rep.count(key); } + + std::pair + equal_range(const key_type& key) const { return rep.equal_range(key); } + +#if !defined(SPP_NO_CXX11_VARIADIC_TEMPLATES) + template + std::pair emplace(Args&&... args) + { + return rep.emplace(std::forward(args)...); + } + + template + iterator emplace_hint(const_iterator , Args&&... args) + { + return rep.emplace(std::forward(args)...).first; + } +#endif + + // Insert + // ------ + std::pair insert(const value_type& obj) + { + std::pair p = rep.insert(obj); + return std::pair(p.first, p.second); // const to non-const + } + +#if !defined(SPP_NO_CXX11_RVALUE_REFERENCES) + template + std::pair insert(P&& obj) { return rep.insert(std::forward

(obj)); } +#endif + + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + + iterator insert(iterator /*unused*/, const value_type& obj) { return insert(obj).first; } + + // Deleted key - do nothing - just to keep google test framework happy + // ------------------------------------------------------------------- + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // Erase + // ----- + size_type erase(const key_type& key) { return rep.erase(key); } + iterator erase(iterator it) { return rep.erase(it); } + iterator erase(iterator f, iterator l) { return rep.erase(f, l); } + + // Comparison + // ---------- + bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + // + // For maximum flexibility, this does not assume a particular + // file type (though it will probably be a FILE *). We just pass + // the fp through to rep. + + // If your keys and values are simple enough, you can pass this + // serializer to serialize()/unserialize(). "Simple enough" means + // value_type is a POD type that contains no pointers. Note, + // however, we don't try to normalize endianness. + // --------------------------------------------------------------- + typedef typename ht::NopointerSerializer NopointerSerializer; + + // serializer: a class providing operator()(OUTPUT*, const value_type&) + // (writing value_type to OUTPUT). You can specify a + // NopointerSerializer object if appropriate (see above). + // fp: either a FILE*, OR an ostream*/subclass_of_ostream*, OR a + // pointer to a class providing size_t Write(const void*, size_t), + // which writes a buffer into a stream (which fp presumably + // owns) and returns the number of bytes successfully written. + // Note basic_ostream is not currently supported. + // --------------------------------------------------------------- + template + bool serialize(ValueSerializer serializer, OUTPUT* fp) + { + return rep.serialize(serializer, fp); + } + + // serializer: a functor providing operator()(INPUT*, value_type*) + // (reading from INPUT and into value_type). You can specify a + // NopointerSerializer object if appropriate (see above). + // fp: either a FILE*, OR an istream*/subclass_of_istream*, OR a + // pointer to a class providing size_t Read(void*, size_t), + // which reads into a buffer from a stream (which fp presumably + // owns) and returns the number of bytes successfully read. + // Note basic_istream is not currently supported. + // NOTE: Since value_type is const Key, ValueSerializer + // may need to do a const cast in order to fill in the key. + // NOTE: if Key is not a POD type, the serializer MUST use + // placement-new to initialize its value, rather than a normal + // equals-assignment or similar. (The value_type* passed into + // the serializer points to garbage memory.) + // --------------------------------------------------------------- + template + bool unserialize(ValueSerializer serializer, INPUT* fp) + { + return rep.unserialize(serializer, fp); + } + + // The four methods below are DEPRECATED. + // Use serialize() and unserialize() for new code. + // ----------------------------------------------- + template + bool write_metadata(OUTPUT *fp) { return rep.write_metadata(fp); } + + template + bool read_metadata(INPUT *fp) { return rep.read_metadata(fp); } + + template + bool write_nopointer_data(OUTPUT *fp) { return rep.write_nopointer_data(fp); } + + template + bool read_nopointer_data(INPUT *fp) { return rep.read_nopointer_data(fp); } + +private: + // The actual data + // --------------- + ht rep; +}; + +} // spp_ namespace + + +// We need a global swap for all our classes as well +// ------------------------------------------------- + +template +inline void swap(spp_::sparsegroup &x, spp_::sparsegroup &y) +{ + x.swap(y); +} + +template +inline void swap(spp_::sparsetable &x, spp_::sparsetable &y) +{ + x.swap(y); +} + +template +inline void swap(spp_::sparse_hashtable &x, + spp_::sparse_hashtable &y) +{ + x.swap(y); +} + +template +inline void swap(spp_::sparse_hash_map& hm1, + spp_::sparse_hash_map& hm2) +{ + hm1.swap(hm2); +} + +template +inline void swap(spp_::sparse_hash_set& hs1, + spp_::sparse_hash_set& hs2) +{ + hs1.swap(hs2); +} + +#endif // sparsepp_h_guard_ diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_config.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_config.h new file mode 100644 index 0000000..46eeee5 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_config.h @@ -0,0 +1,781 @@ +#if !defined(spp_config_h_guard) +#define spp_config_h_guard + +// -------------------------------------------------- +// Sparsepp config macros +// some can be overriden on the command line +// -------------------------------------------------- +#ifndef SPP_NAMESPACE + #define SPP_NAMESPACE spp +#endif + +#ifndef spp_ + #define spp_ SPP_NAMESPACE +#endif + +#ifndef SPP_DEFAULT_ALLOCATOR + #if (defined(SPP_USE_SPP_ALLOC) && SPP_USE_SPP_ALLOC) && defined(_MSC_VER) + // ----------------------------------------------------------------------------- + // When building with the Microsoft compiler, we use a custom allocator because + // the default one fragments memory when reallocating. This is desirable only + // when creating large sparsepp hash maps. If you create lots of small hash_maps, + // define the following before including spp.h: + // #define SPP_DEFAULT_ALLOCATOR spp::libc_allocator + // ----------------------------------------------------------------------------- + #define SPP_DEFAULT_ALLOCATOR spp_::spp_allocator + #define SPP_INCLUDE_SPP_ALLOC + #else + #define SPP_DEFAULT_ALLOCATOR spp_::libc_allocator + #endif +#endif + +#ifndef SPP_GROUP_SIZE + // must be 32 or 64 + #define SPP_GROUP_SIZE 32 +#endif + +#ifndef SPP_ALLOC_SZ + // must be power of 2 (0 = agressive alloc, 1 = smallest memory usage, 2 = good compromise) + #define SPP_ALLOC_SZ 0 +#endif + +#ifndef SPP_STORE_NUM_ITEMS + // 1 uses a little bit more memory, but faster!! + #define SPP_STORE_NUM_ITEMS 1 +#endif + + +// --------------------------------------------------------------------------- +// Compiler detection code (SPP_ proprocessor macros) derived from Boost +// libraries. Therefore Boost software licence reproduced below. +// --------------------------------------------------------------------------- +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// --------------------------------------------------------------------------- + +// Boost like configuration +// ------------------------ +#if defined __clang__ + + #if defined(i386) + #include + inline void spp_cpuid(int info[4], int InfoType) { + __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); + } + #endif + + #define SPP_POPCNT __builtin_popcount + #define SPP_POPCNT64 __builtin_popcountll + + #define SPP_HAS_CSTDINT + + #ifndef __has_extension + #define __has_extension __has_feature + #endif + + #if !__has_feature(cxx_exceptions) && !defined(SPP_NO_EXCEPTIONS) + #define SPP_NO_EXCEPTIONS + #endif + + #if !__has_feature(cxx_rtti) && !defined(SPP_NO_RTTI) + #define SPP_NO_RTTI + #endif + + #if !__has_feature(cxx_rtti) && !defined(SPP_NO_TYPEID) + #define SPP_NO_TYPEID + #endif + + #if defined(__int64) && !defined(__GNUC__) + #define SPP_HAS_MS_INT64 + #endif + + #define SPP_HAS_NRVO + + // Branch prediction hints + #if defined(__has_builtin) + #if __has_builtin(__builtin_expect) + #define SPP_LIKELY(x) __builtin_expect(x, 1) + #define SPP_UNLIKELY(x) __builtin_expect(x, 0) + #endif + #endif + + // Clang supports "long long" in all compilation modes. + #define SPP_HAS_LONG_LONG + + #if !__has_feature(cxx_constexpr) + #define SPP_NO_CXX11_CONSTEXPR + #endif + + #if !__has_feature(cxx_decltype) + #define SPP_NO_CXX11_DECLTYPE + #endif + + #if !__has_feature(cxx_decltype_incomplete_return_types) + #define SPP_NO_CXX11_DECLTYPE_N3276 + #endif + + #if !__has_feature(cxx_defaulted_functions) + #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS + #endif + + #if !__has_feature(cxx_deleted_functions) + #define SPP_NO_CXX11_DELETED_FUNCTIONS + #endif + + #if !__has_feature(cxx_explicit_conversions) + #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS + #endif + + #if !__has_feature(cxx_default_function_template_args) + #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS + #endif + + #if !__has_feature(cxx_generalized_initializers) + #define SPP_NO_CXX11_HDR_INITIALIZER_LIST + #endif + + #if !__has_feature(cxx_lambdas) + #define SPP_NO_CXX11_LAMBDAS + #endif + + #if !__has_feature(cxx_local_type_template_args) + #define SPP_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS + #endif + + #if !__has_feature(cxx_raw_string_literals) + #define SPP_NO_CXX11_RAW_LITERALS + #endif + + #if !__has_feature(cxx_reference_qualified_functions) + #define SPP_NO_CXX11_REF_QUALIFIERS + #endif + + #if !__has_feature(cxx_generalized_initializers) + #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX + #endif + + #if !__has_feature(cxx_rvalue_references) + #define SPP_NO_CXX11_RVALUE_REFERENCES + #endif + + #if !__has_feature(cxx_static_assert) + #define SPP_NO_CXX11_STATIC_ASSERT + #endif + + #if !__has_feature(cxx_alias_templates) + #define SPP_NO_CXX11_TEMPLATE_ALIASES + #endif + + #if !__has_feature(cxx_variadic_templates) + #define SPP_NO_CXX11_VARIADIC_TEMPLATES + #endif + + #if !__has_feature(cxx_user_literals) + #define SPP_NO_CXX11_USER_DEFINED_LITERALS + #endif + + #if !__has_feature(cxx_alignas) + #define SPP_NO_CXX11_ALIGNAS + #endif + + #if !__has_feature(cxx_trailing_return) + #define SPP_NO_CXX11_TRAILING_RESULT_TYPES + #endif + + #if !__has_feature(cxx_inline_namespaces) + #define SPP_NO_CXX11_INLINE_NAMESPACES + #endif + + #if !__has_feature(cxx_override_control) + #define SPP_NO_CXX11_FINAL + #endif + + #if !(__has_feature(__cxx_binary_literals__) || __has_extension(__cxx_binary_literals__)) + #define SPP_NO_CXX14_BINARY_LITERALS + #endif + + #if !__has_feature(__cxx_decltype_auto__) + #define SPP_NO_CXX14_DECLTYPE_AUTO + #endif + + #if !__has_feature(__cxx_init_captures__) + #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + #endif + + #if !__has_feature(__cxx_generic_lambdas__) + #define SPP_NO_CXX14_GENERIC_LAMBDAS + #endif + + + #if !__has_feature(__cxx_generic_lambdas__) || !__has_feature(__cxx_relaxed_constexpr__) + #define SPP_NO_CXX14_CONSTEXPR + #endif + + #if !__has_feature(__cxx_return_type_deduction__) + #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION + #endif + + #if !__has_feature(__cxx_variable_templates__) + #define SPP_NO_CXX14_VARIABLE_TEMPLATES + #endif + + #if __cplusplus < 201400 + #define SPP_NO_CXX14_DIGIT_SEPARATORS + #endif + + #if defined(__has_builtin) && __has_builtin(__builtin_unreachable) + #define SPP_UNREACHABLE_RETURN(x) __builtin_unreachable(); + #endif + + #define SPP_ATTRIBUTE_UNUSED __attribute__((__unused__)) + + #ifndef SPP_COMPILER + #define SPP_COMPILER "Clang version " __clang_version__ + #endif + + #define SPP_CLANG 1 + + +#elif defined __GNUC__ + + #define SPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + + // definition to expand macro then apply to pragma message + // #define VALUE_TO_STRING(x) #x + // #define VALUE(x) VALUE_TO_STRING(x) + // #define VAR_NAME_VALUE(var) #var "=" VALUE(var) + // #pragma message(VAR_NAME_VALUE(SPP_GCC_VERSION)) + + #if defined(i386) + #include + inline void spp_cpuid(int info[4], int InfoType) { + __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); + } + #endif + + // __POPCNT__ defined when the compiled with popcount support + // (-mpopcnt compiler option is given for example) + #ifdef __POPCNT__ + // slower unless compiled iwith -mpopcnt + #define SPP_POPCNT __builtin_popcount + #define SPP_POPCNT64 __builtin_popcountll + #endif + + #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) + #define SPP_GCC_CXX11 + #endif + + #if __GNUC__ == 3 + #if defined (__PATHSCALE__) + #define SPP_NO_TWO_PHASE_NAME_LOOKUP + #define SPP_NO_IS_ABSTRACT + #endif + + #if __GNUC_MINOR__ < 4 + #define SPP_NO_IS_ABSTRACT + #endif + + #define SPP_NO_CXX11_EXTERN_TEMPLATE + #endif + + #if __GNUC__ < 4 + // + // All problems to gcc-3.x and earlier here: + // + #define SPP_NO_TWO_PHASE_NAME_LOOKUP + #ifdef __OPEN64__ + #define SPP_NO_IS_ABSTRACT + #endif + #endif + + // GCC prior to 3.4 had #pragma once too but it didn't work well with filesystem links + #if SPP_GCC_VERSION >= 30400 + #define SPP_HAS_PRAGMA_ONCE + #endif + + #if SPP_GCC_VERSION < 40400 + // Previous versions of GCC did not completely implement value-initialization: + // GCC Bug 30111, "Value-initialization of POD base class doesn't initialize + // members", reported by Jonathan Wakely in 2006, + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 (fixed for GCC 4.4) + // GCC Bug 33916, "Default constructor fails to initialize array members", + // reported by Michael Elizabeth Chastain in 2007, + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 (fixed for GCC 4.2.4) + // See also: http://www.boost.org/libs/utility/value_init.htm #compiler_issues + #define SPP_NO_COMPLETE_VALUE_INITIALIZATION + #endif + + #if !defined(__EXCEPTIONS) && !defined(SPP_NO_EXCEPTIONS) + #define SPP_NO_EXCEPTIONS + #endif + + // + // Threading support: Turn this on unconditionally here (except for + // those platforms where we can know for sure). It will get turned off again + // later if no threading API is detected. + // + #if !defined(__MINGW32__) && !defined(linux) && !defined(__linux) && !defined(__linux__) + #define SPP_HAS_THREADS + #endif + + // + // gcc has "long long" + // Except on Darwin with standard compliance enabled (-pedantic) + // Apple gcc helpfully defines this macro we can query + // + #if !defined(__DARWIN_NO_LONG_LONG) + #define SPP_HAS_LONG_LONG + #endif + + // + // gcc implements the named return value optimization since version 3.1 + // + #define SPP_HAS_NRVO + + // Branch prediction hints + #define SPP_LIKELY(x) __builtin_expect(x, 1) + #define SPP_UNLIKELY(x) __builtin_expect(x, 0) + + // + // Dynamic shared object (DSO) and dynamic-link library (DLL) support + // + #if __GNUC__ >= 4 + #if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && !defined(__CYGWIN__) + // All Win32 development environments, including 64-bit Windows and MinGW, define + // _WIN32 or one of its variant spellings. Note that Cygwin is a POSIX environment, + // so does not define _WIN32 or its variants. + #define SPP_HAS_DECLSPEC + #define SPP_SYMBOL_EXPORT __attribute__((__dllexport__)) + #define SPP_SYMBOL_IMPORT __attribute__((__dllimport__)) + #else + #define SPP_SYMBOL_EXPORT __attribute__((__visibility__("default"))) + #define SPP_SYMBOL_IMPORT + #endif + + #define SPP_SYMBOL_VISIBLE __attribute__((__visibility__("default"))) + #else + // config/platform/win32.hpp will define SPP_SYMBOL_EXPORT, etc., unless already defined + #define SPP_SYMBOL_EXPORT + #endif + + // + // RTTI and typeinfo detection is possible post gcc-4.3: + // + #if SPP_GCC_VERSION > 40300 + #ifndef __GXX_RTTI + #ifndef SPP_NO_TYPEID + #define SPP_NO_TYPEID + #endif + #ifndef SPP_NO_RTTI + #define SPP_NO_RTTI + #endif + #endif + #endif + + // + // Recent GCC versions have __int128 when in 64-bit mode. + // + // We disable this if the compiler is really nvcc with C++03 as it + // doesn't actually support __int128 as of CUDA_VERSION=7500 + // even though it defines __SIZEOF_INT128__. + // See https://svn.boost.org/trac/boost/ticket/8048 + // https://svn.boost.org/trac/boost/ticket/11852 + // Only re-enable this for nvcc if you're absolutely sure + // of the circumstances under which it's supported: + // + #if defined(__CUDACC__) + #if defined(SPP_GCC_CXX11) + #define SPP_NVCC_CXX11 + #else + #define SPP_NVCC_CXX03 + #endif + #endif + + #if defined(__SIZEOF_INT128__) && !defined(SPP_NVCC_CXX03) + #define SPP_HAS_INT128 + #endif + // + // Recent GCC versions have a __float128 native type, we need to + // include a std lib header to detect this - not ideal, but we'll + // be including later anyway when we select the std lib. + // + // Nevertheless, as of CUDA 7.5, using __float128 with the host + // compiler in pre-C++11 mode is still not supported. + // See https://svn.boost.org/trac/boost/ticket/11852 + // + #ifdef __cplusplus + #include + #else + #include + #endif + + #if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) && !defined(SPP_NVCC_CXX03) + #define SPP_HAS_FLOAT128 + #endif + + // C++0x features in 4.3.n and later + // + #if (SPP_GCC_VERSION >= 40300) && defined(SPP_GCC_CXX11) + // C++0x features are only enabled when -std=c++0x or -std=gnu++0x are + // passed on the command line, which in turn defines + // __GXX_EXPERIMENTAL_CXX0X__. + #define SPP_HAS_DECLTYPE + #define SPP_HAS_RVALUE_REFS + #define SPP_HAS_STATIC_ASSERT + #define SPP_HAS_VARIADIC_TMPL + #define SPP_HAS_CSTDINT + #else + #define SPP_NO_CXX11_DECLTYPE + #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS + #define SPP_NO_CXX11_RVALUE_REFERENCES + #define SPP_NO_CXX11_STATIC_ASSERT + #endif + + // C++0x features in 4.4.n and later + // + #if (SPP_GCC_VERSION < 40400) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_AUTO_DECLARATIONS + #define SPP_NO_CXX11_AUTO_MULTIDECLARATIONS + #define SPP_NO_CXX11_CHAR16_T + #define SPP_NO_CXX11_CHAR32_T + #define SPP_NO_CXX11_HDR_INITIALIZER_LIST + #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS + #define SPP_NO_CXX11_DELETED_FUNCTIONS + #define SPP_NO_CXX11_TRAILING_RESULT_TYPES + #define SPP_NO_CXX11_INLINE_NAMESPACES + #define SPP_NO_CXX11_VARIADIC_TEMPLATES + #endif + + #if SPP_GCC_VERSION < 40500 + #define SPP_NO_SFINAE_EXPR + #endif + + // GCC 4.5 forbids declaration of defaulted functions in private or protected sections + #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 5) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS + #endif + + // C++0x features in 4.5.0 and later + // + #if (SPP_GCC_VERSION < 40500) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS + #define SPP_NO_CXX11_LAMBDAS + #define SPP_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS + #define SPP_NO_CXX11_RAW_LITERALS + #endif + + // C++0x features in 4.6.n and later + // + #if (SPP_GCC_VERSION < 40600) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_CONSTEXPR + #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX + #endif + + // C++0x features in 4.7.n and later + // + #if (SPP_GCC_VERSION < 40700) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_FINAL + #define SPP_NO_CXX11_TEMPLATE_ALIASES + #define SPP_NO_CXX11_USER_DEFINED_LITERALS + #define SPP_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS + #endif + + // C++0x features in 4.8.n and later + // + #if (SPP_GCC_VERSION < 40800) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_ALIGNAS + #endif + + // C++0x features in 4.8.1 and later + // + #if (SPP_GCC_VERSION < 40801) || !defined(SPP_GCC_CXX11) + #define SPP_NO_CXX11_DECLTYPE_N3276 + #define SPP_NO_CXX11_REF_QUALIFIERS + #define SPP_NO_CXX14_BINARY_LITERALS + #endif + + // C++14 features in 4.9.0 and later + // + #if (SPP_GCC_VERSION < 40900) || (__cplusplus < 201300) + #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION + #define SPP_NO_CXX14_GENERIC_LAMBDAS + #define SPP_NO_CXX14_DIGIT_SEPARATORS + #define SPP_NO_CXX14_DECLTYPE_AUTO + #if !((SPP_GCC_VERSION >= 40801) && (SPP_GCC_VERSION < 40900) && defined(SPP_GCC_CXX11)) + #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + #endif + #endif + + + // C++ 14: + #if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) + #define SPP_NO_CXX14_CONSTEXPR + #endif + #if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) + #define SPP_NO_CXX14_VARIABLE_TEMPLATES + #endif + + // + // Unused attribute: + #if __GNUC__ >= 4 + #define SPP_ATTRIBUTE_UNUSED __attribute__((__unused__)) + #endif + // + // __builtin_unreachable: + #if SPP_GCC_VERSION >= 40800 + #define SPP_UNREACHABLE_RETURN(x) __builtin_unreachable(); + #endif + + #ifndef SPP_COMPILER + #define SPP_COMPILER "GNU C++ version " __VERSION__ + #endif + + // ConceptGCC compiler: + // http://www.generic-programming.org/software/ConceptGCC/ + #ifdef __GXX_CONCEPTS__ + #define SPP_HAS_CONCEPTS + #define SPP_COMPILER "ConceptGCC version " __VERSION__ + #endif + +#elif defined _MSC_VER + + #include // for __popcnt() + + #define SPP_POPCNT_CHECK // slower when defined, but we have to check! + #define spp_cpuid(info, x) __cpuid(info, x) + + #define SPP_POPCNT __popcnt + #if (SPP_GROUP_SIZE == 64 && INTPTR_MAX == INT64_MAX) + #define SPP_POPCNT64 __popcnt64 + #endif + + // Attempt to suppress VC6 warnings about the length of decorated names (obsolete): + #pragma warning( disable : 4503 ) // warning: decorated name length exceeded + + #define SPP_HAS_PRAGMA_ONCE + #define SPP_HAS_CSTDINT + + // + // versions check: + // we don't support Visual C++ prior to version 7.1: + #if _MSC_VER < 1310 + #error "Antique compiler not supported" + #endif + + #if _MSC_FULL_VER < 180020827 + #define SPP_NO_FENV_H + #endif + + #if _MSC_VER < 1400 + // although a conforming signature for swprint exists in VC7.1 + // it appears not to actually work: + #define SPP_NO_SWPRINTF + + // Our extern template tests also fail for this compiler: + #define SPP_NO_CXX11_EXTERN_TEMPLATE + + // Variadic macros do not exist for VC7.1 and lower + #define SPP_NO_CXX11_VARIADIC_MACROS + #endif + + #if _MSC_VER < 1500 // 140X == VC++ 8.0 + #undef SPP_HAS_CSTDINT + #define SPP_NO_MEMBER_TEMPLATE_FRIENDS + #endif + + #if _MSC_VER < 1600 // 150X == VC++ 9.0 + // A bug in VC9: + #define SPP_NO_ADL_BARRIER + #endif + + + // MSVC (including the latest checked version) has not yet completely + // implemented value-initialization, as is reported: + // "VC++ does not value-initialize members of derived classes without + // user-declared constructor", reported in 2009 by Sylvester Hesp: + // https: //connect.microsoft.com/VisualStudio/feedback/details/484295 + // "Presence of copy constructor breaks member class initialization", + // reported in 2009 by Alex Vakulenko: + // https: //connect.microsoft.com/VisualStudio/feedback/details/499606 + // "Value-initialization in new-expression", reported in 2005 by + // Pavel Kuznetsov (MetaCommunications Engineering): + // https: //connect.microsoft.com/VisualStudio/feedback/details/100744 + // See also: http: //www.boost.org/libs/utility/value_init.htm #compiler_issues + // (Niels Dekker, LKEB, May 2010) + #define SPP_NO_COMPLETE_VALUE_INITIALIZATION + + #ifndef _NATIVE_WCHAR_T_DEFINED + #define SPP_NO_INTRINSIC_WCHAR_T + #endif + + // + // check for exception handling support: + #if !defined(_CPPUNWIND) && !defined(SPP_NO_EXCEPTIONS) + #define SPP_NO_EXCEPTIONS + #endif + + // + // __int64 support: + // + #define SPP_HAS_MS_INT64 + #if defined(_MSC_EXTENSIONS) || (_MSC_VER >= 1400) + #define SPP_HAS_LONG_LONG + #else + #define SPP_NO_LONG_LONG + #endif + + #if (_MSC_VER >= 1400) && !defined(_DEBUG) + #define SPP_HAS_NRVO + #endif + + #if _MSC_VER >= 1500 // 150X == VC++ 9.0 + #define SPP_HAS_PRAGMA_DETECT_MISMATCH + #endif + + // + // disable Win32 API's if compiler extensions are + // turned off: + // + #if !defined(_MSC_EXTENSIONS) && !defined(SPP_DISABLE_WIN32) + #define SPP_DISABLE_WIN32 + #endif + + #if !defined(_CPPRTTI) && !defined(SPP_NO_RTTI) + #define SPP_NO_RTTI + #endif + + // + // TR1 features: + // + #if _MSC_VER >= 1700 + // #define SPP_HAS_TR1_HASH // don't know if this is true yet. + // #define SPP_HAS_TR1_TYPE_TRAITS // don't know if this is true yet. + #define SPP_HAS_TR1_UNORDERED_MAP + #define SPP_HAS_TR1_UNORDERED_SET + #endif + + // + // C++0x features + // + // See above for SPP_NO_LONG_LONG + + // C++ features supported by VC++ 10 (aka 2010) + // + #if _MSC_VER < 1600 + #define SPP_NO_CXX11_AUTO_DECLARATIONS + #define SPP_NO_CXX11_AUTO_MULTIDECLARATIONS + #define SPP_NO_CXX11_LAMBDAS + #define SPP_NO_CXX11_RVALUE_REFERENCES + #define SPP_NO_CXX11_STATIC_ASSERT + #define SPP_NO_CXX11_DECLTYPE + #endif // _MSC_VER < 1600 + + #if _MSC_VER >= 1600 + #define SPP_HAS_STDINT_H + #endif + + // C++11 features supported by VC++ 11 (aka 2012) + // + #if _MSC_VER < 1700 + #define SPP_NO_CXX11_FINAL + #endif // _MSC_VER < 1700 + + // C++11 features supported by VC++ 12 (aka 2013). + // + #if _MSC_FULL_VER < 180020827 + #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS + #define SPP_NO_CXX11_DELETED_FUNCTIONS + #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS + #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS + #define SPP_NO_CXX11_RAW_LITERALS + #define SPP_NO_CXX11_TEMPLATE_ALIASES + #define SPP_NO_CXX11_TRAILING_RESULT_TYPES + #define SPP_NO_CXX11_VARIADIC_TEMPLATES + #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX + #define SPP_NO_CXX11_DECLTYPE_N3276 + #endif + + // C++11 features supported by VC++ 14 (aka 2014) CTP1 + #if (_MSC_FULL_VER < 190021730) + #define SPP_NO_CXX11_REF_QUALIFIERS + #define SPP_NO_CXX11_USER_DEFINED_LITERALS + #define SPP_NO_CXX11_ALIGNAS + #define SPP_NO_CXX11_INLINE_NAMESPACES + #define SPP_NO_CXX14_DECLTYPE_AUTO + #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES + #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION + #define SPP_NO_CXX11_HDR_INITIALIZER_LIST + #endif + + // C++11 features not supported by any versions + #define SPP_NO_CXX11_CHAR16_T + #define SPP_NO_CXX11_CHAR32_T + #define SPP_NO_CXX11_CONSTEXPR + #define SPP_NO_SFINAE_EXPR + #define SPP_NO_TWO_PHASE_NAME_LOOKUP + + // C++ 14: + #if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) + #define SPP_NO_CXX14_BINARY_LITERALS + #endif + + #if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) + #define SPP_NO_CXX14_CONSTEXPR + #endif + + #if (__cplusplus < 201304) // There's no SD6 check for this.... + #define SPP_NO_CXX14_DIGIT_SEPARATORS + #endif + + #if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) + #define SPP_NO_CXX14_GENERIC_LAMBDAS + #endif + + #if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) + #define SPP_NO_CXX14_VARIABLE_TEMPLATES + #endif + +#endif + +// from boost/config/suffix.hpp +// ---------------------------- +#ifndef SPP_ATTRIBUTE_UNUSED + #define SPP_ATTRIBUTE_UNUSED +#endif + +/* + Try to persuade compilers to inline. +*/ +#ifndef SPP_FORCEINLINE + #if defined(__GNUC__) + #define SPP_FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define SPP_FORCEINLINE __forceinline + #else + #define SPP_FORCEINLINE inline + #endif +#endif + + +#endif // spp_config_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_dlalloc.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_dlalloc.h new file mode 100644 index 0000000..f88aab7 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_dlalloc.h @@ -0,0 +1,4044 @@ +#ifndef spp_dlalloc__h_ +#define spp_dlalloc__h_ + +/* This is a C++ allocator created from Doug Lea's dlmalloc + (Version 2.8.6 Wed Aug 29 06:57:58 2012) + see: http://g.oswego.edu/dl/html/malloc.html +*/ + +#include "spp_utils.h" +#include "spp_smartptr.h" + + +#ifndef SPP_FORCEINLINE + #if defined(__GNUC__) + #define SPP_FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define SPP_FORCEINLINE __forceinline + #else + #define SPP_FORCEINLINE inline + #endif +#endif + + +#ifndef SPP_IMPL + #define SPP_IMPL SPP_FORCEINLINE +#endif + +#ifndef SPP_API + #define SPP_API static +#endif + + +namespace spp +{ + // ---------------------- allocator internal API ----------------------- + typedef void* mspace; + + /* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different SPP_DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). + */ + SPP_API mspace create_mspace(size_t capacity, int locked); + SPP_API size_t destroy_mspace(mspace msp); + SPP_API void* mspace_malloc(mspace msp, size_t bytes); + SPP_API void mspace_free(mspace msp, void* mem); + SPP_API void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +#if 0 + SPP_API mspace create_mspace_with_base(void* base, size_t capacity, int locked); + SPP_API int mspace_track_large_chunks(mspace msp, int enable); + SPP_API void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + SPP_API void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + SPP_API void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + SPP_API void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + SPP_API size_t mspace_footprint(mspace msp); + SPP_API size_t mspace_max_footprint(mspace msp); + SPP_API size_t mspace_usable_size(const void* mem); + SPP_API int mspace_trim(mspace msp, size_t pad); + SPP_API int mspace_mallopt(int, int); +#endif + + // ----------------------------------------------------------- + // ----------------------------------------------------------- + struct MSpace : public spp_rc + { + MSpace() : + _sp(create_mspace(0, 0)) + {} + + ~MSpace() + { + destroy_mspace(_sp); + } + + mspace _sp; + }; + + // ----------------------------------------------------------- + // ----------------------------------------------------------- + template + class spp_allocator + { + public: + typedef T value_type; + typedef T* pointer; + typedef ptrdiff_t difference_type; + typedef const T* const_pointer; + typedef size_t size_type; + + MSpace *getSpace() const { return _space.get(); } + + spp_allocator() : _space(new MSpace) {} + + template + spp_allocator(const spp_allocator &o) : _space(o.getSpace()) {} + + template + spp_allocator& operator=(const spp_allocator &o) + { + if (&o != this) + _space = o.getSpace(); + return *this; + } + + void swap(spp_allocator &o) + { + std::swap(_space, o._space); + } + + pointer allocate(size_t n, const_pointer /* unused */ = 0) + { + pointer res = static_cast(mspace_malloc(_space->_sp, n * sizeof(T))); + if (!res) + throw std::bad_alloc(); + return res; + } + + void deallocate(pointer p, size_t /* unused */) + { + mspace_free(_space->_sp, p); + } + + pointer reallocate(pointer p, size_t new_size) + { + pointer res = static_cast(mspace_realloc(_space->_sp, p, new_size * sizeof(T))); + if (!res) + throw std::bad_alloc(); + return res; + } + + pointer reallocate(pointer p, size_type /* old_size */, size_t new_size) + { + return reallocate(p, new_size); + } + + size_type max_size() const + { + return static_cast(-1) / sizeof(value_type); + } + + void construct(pointer p, const value_type& val) + { + new (p) value_type(val); + } + + void destroy(pointer p) { p->~value_type(); } + + template + struct rebind + { + // rebind to libc_allocator because we want to use malloc_inspect_all in destructive_iterator + // to reduce peak memory usage (we don't want mixed with value_type when + // we traverse the allocated memory). + typedef spp::spp_allocator other; + }; + + mspace space() const { return _space->_sp; } + + // check if we can clear the whole allocator memory at once => works only if the allocator + // is not be shared. If can_clear() returns true, we expect that the next allocator call + // will be clear() - not allocate() or deallocate() + bool can_clear() + { + assert(!_space_to_clear); + _space_to_clear.reset(); + _space_to_clear.swap(_space); + if (_space_to_clear->count() == 1) + return true; + else + _space_to_clear.swap(_space); + return false; + } + + void clear() + { + assert(!_space && !!_space_to_clear); + _space_to_clear.reset(); + _space = new MSpace; + } + + private: + spp_sptr _space; + spp_sptr _space_to_clear; + }; +} + + +// allocators are "equal" whenever memory allocated with one can be deallocated with the other +template +inline bool operator==(const spp_::spp_allocator &a, const spp_::spp_allocator &b) +{ + return a.space() == b.space(); +} + +template +inline bool operator!=(const spp_::spp_allocator &a, const spp_::spp_allocator &b) +{ + return !(a == b); +} + +namespace std +{ + template + inline void swap(spp_::spp_allocator &a, spp_::spp_allocator &b) + { + a.swap(b); + } +} + +#if !defined(SPP_EXCLUDE_IMPLEMENTATION) + +#ifndef WIN32 + #ifdef _WIN32 + #define WIN32 1 + #endif + #ifdef _WIN32_WCE + #define SPP_LACKS_FCNTL_H + #define WIN32 1 + #endif +#endif + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + #define SPP_HAVE_MMAP 1 + #define SPP_LACKS_UNISTD_H + #define SPP_LACKS_SYS_PARAM_H + #define SPP_LACKS_SYS_MMAN_H + #define SPP_LACKS_STRING_H + #define SPP_LACKS_STRINGS_H + #define SPP_LACKS_SYS_TYPES_H + #define SPP_LACKS_ERRNO_H + #define SPP_LACKS_SCHED_H + #ifndef SPP_MALLOC_FAILURE_ACTION + #define SPP_MALLOC_FAILURE_ACTION + #endif + #ifndef SPP_MMAP_CLEARS + #ifdef _WIN32_WCE /* WINCE reportedly does not clear */ + #define SPP_MMAP_CLEARS 0 + #else + #define SPP_MMAP_CLEARS 1 + #endif + #endif +#endif + +#if defined(DARWIN) || defined(_DARWIN) + #define SPP_HAVE_MMAP 1 + /* OSX allocators provide 16 byte alignment */ + #ifndef SPP_MALLOC_ALIGNMENT + #define SPP_MALLOC_ALIGNMENT ((size_t)16U) + #endif +#endif + +#ifndef SPP_LACKS_SYS_TYPES_H + #include /* For size_t */ +#endif + +#ifndef SPP_MALLOC_ALIGNMENT + #define SPP_MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) +#endif + +/* ------------------- size_t and alignment properties -------------------- */ +static const size_t spp_max_size_t = ~(size_t)0; +static const size_t spp_size_t_bitsize = sizeof(size_t) << 3; +static const size_t spp_half_max_size_t = spp_max_size_t / 2U; +static const size_t spp_chunk_align_mask = SPP_MALLOC_ALIGNMENT - 1; + +#if defined(SPP_DEBUG) || !defined(NDEBUG) +static bool spp_is_aligned(void *p) { return ((size_t)p & spp_chunk_align_mask) == 0; } +#endif + +// the number of bytes to offset an address to align it +static size_t align_offset(void *p) +{ + return (((size_t)p & spp_chunk_align_mask) == 0) ? 0 : + ((SPP_MALLOC_ALIGNMENT - ((size_t)p & spp_chunk_align_mask)) & spp_chunk_align_mask); +} + + +#ifndef SPP_FOOTERS + #define SPP_FOOTERS 0 +#endif + +#ifndef SPP_ABORT + #define SPP_ABORT abort() +#endif + +#ifndef SPP_ABORT_ON_ASSERT_FAILURE + #define SPP_ABORT_ON_ASSERT_FAILURE 1 +#endif + +#ifndef SPP_PROCEED_ON_ERROR + #define SPP_PROCEED_ON_ERROR 0 +#endif + +#ifndef SPP_INSECURE + #define SPP_INSECURE 0 +#endif + +#ifndef SPP_MALLOC_INSPECT_ALL + #define SPP_MALLOC_INSPECT_ALL 0 +#endif + +#ifndef SPP_HAVE_MMAP + #define SPP_HAVE_MMAP 1 +#endif + +#ifndef SPP_MMAP_CLEARS + #define SPP_MMAP_CLEARS 1 +#endif + +#ifndef SPP_HAVE_MREMAP + #ifdef linux + #define SPP_HAVE_MREMAP 1 + #ifndef _GNU_SOURCE + #define _GNU_SOURCE /* Turns on mremap() definition */ + #endif + #else + #define SPP_HAVE_MREMAP 0 + #endif +#endif + +#ifndef SPP_MALLOC_FAILURE_ACTION + // ENOMEM = 12 + #define SPP_MALLOC_FAILURE_ACTION errno = 12 +#endif + + +#ifndef SPP_DEFAULT_GRANULARITY + #if defined(WIN32) + #define SPP_DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ + #else + #define SPP_DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) + #endif +#endif + +#ifndef SPP_DEFAULT_TRIM_THRESHOLD + #define SPP_DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#endif + +#ifndef SPP_DEFAULT_MMAP_THRESHOLD + #if SPP_HAVE_MMAP + #define SPP_DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) + #else + #define SPP_DEFAULT_MMAP_THRESHOLD spp_max_size_t + #endif +#endif + +#ifndef SPP_MAX_RELEASE_CHECK_RATE + #if SPP_HAVE_MMAP + #define SPP_MAX_RELEASE_CHECK_RATE 4095 + #else + #define SPP_MAX_RELEASE_CHECK_RATE spp_max_size_t + #endif +#endif + +#ifndef SPP_USE_BUILTIN_FFS + #define SPP_USE_BUILTIN_FFS 0 +#endif + +#ifndef SPP_USE_DEV_RANDOM + #define SPP_USE_DEV_RANDOM 0 +#endif + +#ifndef SPP_NO_SEGMENT_TRAVERSAL + #define SPP_NO_SEGMENT_TRAVERSAL 0 +#endif + + + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef _MSC_VER + #pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif +#ifndef SPP_LACKS_ERRNO_H + #include /* for SPP_MALLOC_FAILURE_ACTION */ +#endif + +#ifdef SPP_DEBUG + #if SPP_ABORT_ON_ASSERT_FAILURE + #undef assert + #define assert(x) if(!(x)) SPP_ABORT + #else + #include + #endif +#else + #ifndef assert + #define assert(x) + #endif + #define SPP_DEBUG 0 +#endif + +#if !defined(WIN32) && !defined(SPP_LACKS_TIME_H) + #include /* for magic initialization */ +#endif + +#ifndef SPP_LACKS_STDLIB_H + #include /* for abort() */ +#endif + +#ifndef SPP_LACKS_STRING_H + #include /* for memset etc */ +#endif + +#if SPP_USE_BUILTIN_FFS + #ifndef SPP_LACKS_STRINGS_H + #include /* for ffs */ + #endif +#endif + +#if SPP_HAVE_MMAP + #ifndef SPP_LACKS_SYS_MMAN_H + /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ + #if (defined(linux) && !defined(__USE_GNU)) + #define __USE_GNU 1 + #include /* for mmap */ + #undef __USE_GNU + #else + #include /* for mmap */ + #endif + #endif + #ifndef SPP_LACKS_FCNTL_H + #include + #endif +#endif + +#ifndef SPP_LACKS_UNISTD_H + #include /* for sbrk, sysconf */ +#else + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) + extern void* sbrk(ptrdiff_t); + #endif +#endif + +#include + +namespace spp +{ + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 + #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ + extern "C" { + unsigned char _BitScanForward(unsigned long *index, unsigned long mask); + unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); + } + + #define BitScanForward _BitScanForward + #define BitScanReverse _BitScanReverse + #pragma intrinsic(_BitScanForward) + #pragma intrinsic(_BitScanReverse) + #endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef WIN32 + #ifndef malloc_getpagesize + #ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ + #ifndef _SC_PAGE_SIZE + #define _SC_PAGE_SIZE _SC_PAGESIZE + #endif + #endif + #ifdef _SC_PAGE_SIZE + #define malloc_getpagesize sysconf(_SC_PAGE_SIZE) + #else + #if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); + #define malloc_getpagesize getpagesize() + #else + #ifdef WIN32 /* use supplied emulation of getpagesize */ + #define malloc_getpagesize getpagesize() + #else + #ifndef SPP_LACKS_SYS_PARAM_H + #include + #endif + #ifdef EXEC_PAGESIZE + #define malloc_getpagesize EXEC_PAGESIZE + #else + #ifdef NBPG + #ifndef CLSIZE + #define malloc_getpagesize NBPG + #else + #define malloc_getpagesize (NBPG * CLSIZE) + #endif + #else + #ifdef NBPC + #define malloc_getpagesize NBPC + #else + #ifdef PAGESIZE + #define malloc_getpagesize PAGESIZE + #else /* just guess */ + #define malloc_getpagesize ((size_t)4096U) + #endif + #endif + #endif + #endif + #endif + #endif + #endif + #endif +#endif + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If SPP_HAVE_MORECORE or SPP_HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MMAP must return mfail on failure */ +static void *mfail = (void*)spp_max_size_t; +static char *cmfail = (char*)mfail; + +#if SPP_HAVE_MMAP + +#ifndef WIN32 + #define SPP_MUNMAP_DEFAULT(a, s) munmap((a), (s)) + #define SPP_MMAP_PROT (PROT_READ | PROT_WRITE) + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + #define MAP_ANONYMOUS MAP_ANON + #endif + + #ifdef MAP_ANONYMOUS + #define SPP_MMAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS) + #define SPP_MMAP_DEFAULT(s) mmap(0, (s), SPP_MMAP_PROT, SPP_MMAP_FLAGS, -1, 0) + #else /* MAP_ANONYMOUS */ + /* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. + */ + #define SPP_MMAP_FLAGS (MAP_PRIVATE) + static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ + void SPP_MMAP_DEFAULT(size_t s) + { + if (dev_zero_fd < 0) + dev_zero_fd = open("/dev/zero", O_RDWR); + mmap(0, s, SPP_MMAP_PROT, SPP_MMAP_FLAGS, dev_zero_fd, 0); + } + #endif /* MAP_ANONYMOUS */ + + #define SPP_DIRECT_MMAP_DEFAULT(s) SPP_MMAP_DEFAULT(s) + +#else /* WIN32 */ + + /* Win32 MMAP via VirtualAlloc */ + static SPP_FORCEINLINE void* win32mmap(size_t size) + { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0) ? ptr : mfail; + } + + /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ + static SPP_FORCEINLINE void* win32direct_mmap(size_t size) + { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0) ? ptr : mfail; + } + + /* This function supports releasing coalesed segments */ + static SPP_FORCEINLINE int win32munmap(void* ptr, size_t size) + { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) + { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; + } + + #define SPP_MMAP_DEFAULT(s) win32mmap(s) + #define SPP_MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) + #define SPP_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* SPP_HAVE_MMAP */ + +#if SPP_HAVE_MREMAP + #ifndef WIN32 + #define SPP_MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) + #endif +#endif + +/** + * Define SPP_CALL_MMAP/SPP_CALL_MUNMAP/SPP_CALL_DIRECT_MMAP + */ +#if SPP_HAVE_MMAP + #define USE_MMAP_BIT 1 + + #ifdef SPP_MMAP + #define SPP_CALL_MMAP(s) SPP_MMAP(s) + #else + #define SPP_CALL_MMAP(s) SPP_MMAP_DEFAULT(s) + #endif + + #ifdef SPP_MUNMAP + #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP((a), (s)) + #else + #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP_DEFAULT((a), (s)) + #endif + + #ifdef SPP_DIRECT_MMAP + #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP(s) + #else + #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP_DEFAULT(s) + #endif + +#else /* SPP_HAVE_MMAP */ + #define USE_MMAP_BIT 0 + + #define SPP_MMAP(s) mfail + #define SPP_MUNMAP(a, s) (-1) + #define SPP_DIRECT_MMAP(s) mfail + #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP(s) + #define SPP_CALL_MMAP(s) SPP_MMAP(s) + #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP((a), (s)) +#endif + +/** + * Define SPP_CALL_MREMAP + */ +#if SPP_HAVE_MMAP && SPP_HAVE_MREMAP + #ifdef MREMAP + #define SPP_CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else + #define SPP_CALL_MREMAP(addr, osz, nsz, mv) SPP_MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif +#else + #define SPP_CALL_MREMAP(addr, osz, nsz, mv) mfail +#endif + +/* mstate bit set if continguous morecore disabled or failed */ +static const unsigned USE_NONCONTIGUOUS_BIT = 4U; + +/* segment bit set in create_mspace_with_base */ +static const unsigned EXTERN_BIT = 8U; + + +/* --------------------------- flags ------------------------ */ + +static const unsigned PINUSE_BIT = 1; +static const unsigned CINUSE_BIT = 2; +static const unsigned FLAG4_BIT = 4; +static const unsigned INUSE_BITS = (PINUSE_BIT | CINUSE_BIT); +static const unsigned FLAG_BITS = (PINUSE_BIT | CINUSE_BIT | FLAG4_BIT); + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#if SPP_FOOTERS + static const unsigned CHUNK_OVERHEAD = 2 * sizeof(size_t); +#else + static const unsigned CHUNK_OVERHEAD = sizeof(size_t); +#endif + +/* MMapped chunks need a second word of overhead ... */ +static const unsigned SPP_MMAP_CHUNK_OVERHEAD = 2 * sizeof(size_t); + +/* ... and additional padding for fake next-chunk at foot */ +static const unsigned SPP_MMAP_FOOT_PAD = 4 * sizeof(size_t); + +// =============================================================================== +struct malloc_chunk_header +{ + void set_size_and_pinuse_of_free_chunk(size_t s) + { + _head = s | PINUSE_BIT; + set_foot(s); + } + + void set_foot(size_t s) + { + ((malloc_chunk_header *)((char*)this + s))->_prev_foot = s; + } + + // extraction of fields from head words + bool cinuse() const { return !!(_head & CINUSE_BIT); } + bool pinuse() const { return !!(_head & PINUSE_BIT); } + bool flag4inuse() const { return !!(_head & FLAG4_BIT); } + bool is_inuse() const { return (_head & INUSE_BITS) != PINUSE_BIT; } + bool is_mmapped() const { return (_head & INUSE_BITS) == 0; } + + size_t chunksize() const { return _head & ~(FLAG_BITS); } + + void clear_pinuse() { _head &= ~PINUSE_BIT; } + void set_flag4() { _head |= FLAG4_BIT; } + void clear_flag4() { _head &= ~FLAG4_BIT; } + + // Treat space at ptr +/- offset as a chunk + malloc_chunk_header * chunk_plus_offset(size_t s) + { + return (malloc_chunk_header *)((char*)this + s); + } + malloc_chunk_header * chunk_minus_offset(size_t s) + { + return (malloc_chunk_header *)((char*)this - s); + } + + // Ptr to next or previous physical malloc_chunk. + malloc_chunk_header * next_chunk() + { + return (malloc_chunk_header *)((char*)this + (_head & ~FLAG_BITS)); + } + malloc_chunk_header * prev_chunk() + { + return (malloc_chunk_header *)((char*)this - (_prev_foot)); + } + + // extract next chunk's pinuse bit + size_t next_pinuse() { return next_chunk()->_head & PINUSE_BIT; } + + size_t _prev_foot; // Size of previous chunk (if free). + size_t _head; // Size and inuse bits. +}; + +// =============================================================================== +struct malloc_chunk : public malloc_chunk_header +{ + // Set size, pinuse bit, foot, and clear next pinuse + void set_free_with_pinuse(size_t s, malloc_chunk* n) + { + n->clear_pinuse(); + set_size_and_pinuse_of_free_chunk(s); + } + + // Get the internal overhead associated with chunk p + size_t overhead_for() { return is_mmapped() ? SPP_MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD; } + + // Return true if malloced space is not necessarily cleared + bool calloc_must_clear() + { +#if SPP_MMAP_CLEARS + return !is_mmapped(); +#else + return true; +#endif + } + + struct malloc_chunk* _fd; // double links -- used only if free. + struct malloc_chunk* _bk; +}; + +static const unsigned MCHUNK_SIZE = sizeof(malloc_chunk); + +/* The smallest size we can malloc is an aligned minimal chunk */ +static const unsigned MIN_CHUNK_SIZE = (MCHUNK_SIZE + spp_chunk_align_mask) & ~spp_chunk_align_mask; + +typedef malloc_chunk mchunk; +typedef malloc_chunk* mchunkptr; +typedef malloc_chunk_header *hchunkptr; +typedef malloc_chunk* sbinptr; // The type of bins of chunks +typedef unsigned int bindex_t; // Described below +typedef unsigned int binmap_t; // Described below +typedef unsigned int flag_t; // The type of various bit flag sets + +// conversion from malloc headers to user pointers, and back +static SPP_FORCEINLINE void *chunk2mem(const void *p) { return (void *)((char *)p + 2 * sizeof(size_t)); } +static SPP_FORCEINLINE mchunkptr mem2chunk(const void *mem) { return (mchunkptr)((char *)mem - 2 * sizeof(size_t)); } + +// chunk associated with aligned address A +static SPP_FORCEINLINE mchunkptr align_as_chunk(char *A) { return (mchunkptr)(A + align_offset(chunk2mem(A))); } + +// Bounds on request (not chunk) sizes. +static const unsigned MAX_REQUEST = (-MIN_CHUNK_SIZE) << 2; +static const unsigned MIN_REQUEST = MIN_CHUNK_SIZE - CHUNK_OVERHEAD - 1; + +// pad request bytes into a usable size +static SPP_FORCEINLINE size_t pad_request(size_t req) +{ + return (req + CHUNK_OVERHEAD + spp_chunk_align_mask) & ~spp_chunk_align_mask; +} + +// pad request, checking for minimum (but not maximum) +static SPP_FORCEINLINE size_t request2size(size_t req) +{ + return req < MIN_REQUEST ? MIN_CHUNK_SIZE : pad_request(req); +} + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use, unless mmapped, in which case both bits are cleared. + + FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +// Head value for fenceposts +static const unsigned FENCEPOST_HEAD = INUSE_BITS | sizeof(size_t); + + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +// =============================================================================== +struct malloc_tree_chunk : public malloc_chunk_header +{ + malloc_tree_chunk *leftmost_child() + { + return _child[0] ? _child[0] : _child[1]; + } + + + malloc_tree_chunk* _fd; + malloc_tree_chunk* _bk; + + malloc_tree_chunk* _child[2]; + malloc_tree_chunk* _parent; + bindex_t _index; +}; + +typedef malloc_tree_chunk tchunk; +typedef malloc_tree_chunk* tchunkptr; +typedef malloc_tree_chunk* tbinptr; // The type of bins of trees + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If USE_MMAP_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +// =============================================================================== +struct malloc_segment +{ + bool is_mmapped_segment() { return !!(_sflags & USE_MMAP_BIT); } + bool is_extern_segment() { return !!(_sflags & EXTERN_BIT); } + + char* _base; // base address + size_t _size; // allocated size + malloc_segment* _next; // ptr to next segment + flag_t _sflags; // mmap and extern flag +}; + +typedef malloc_segment msegment; +typedef malloc_segment* msegmentptr; + +/* ------------- Malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. Note that the non-zeroness of "magic" + also serves as an initialization flag. +*/ + +// =============================================================================== +struct malloc_params +{ + malloc_params() : _magic(0) {} + + void ensure_initialization() + { + if (!_magic) + _init(); + } + + SPP_IMPL int change(int param_number, int value); + + size_t page_align(size_t sz) + { + return (sz + (_page_size - 1)) & ~(_page_size - 1); + } + + size_t granularity_align(size_t sz) + { + return (sz + (_granularity - 1)) & ~(_granularity - 1); + } + + bool is_page_aligned(char *S) + { + return ((size_t)S & (_page_size - 1)) == 0; + } + + SPP_IMPL int _init(); + + size_t _magic; + size_t _page_size; + size_t _granularity; + size_t _mmap_threshold; + size_t _trim_threshold; + flag_t _default_mflags; +}; + +static malloc_params mparams; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless SPP_INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams._magic. + + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. +*/ + + +// ================================================================================ +class malloc_state +{ +public: + /* ----------------------- _malloc, _free, etc... --- */ + SPP_FORCEINLINE void* _malloc(size_t bytes); + SPP_FORCEINLINE void _free(mchunkptr p); + + + /* ------------------------ Relays to internal calls to malloc/free from realloc, memalign etc */ + void *internal_malloc(size_t b) { return mspace_malloc(this, b); } + void internal_free(void *mem) { mspace_free(this, mem); } + + /* ------------------------ ----------------------- */ + + SPP_IMPL void init_top(mchunkptr p, size_t psize); + SPP_IMPL void init_bins(); + SPP_IMPL void init(char* tbase, size_t tsize); + + /* ------------------------ System alloc/dealloc -------------------------- */ + SPP_IMPL void* sys_alloc(size_t nb); + SPP_IMPL size_t release_unused_segments(); + SPP_IMPL int sys_trim(size_t pad); + SPP_IMPL void dispose_chunk(mchunkptr p, size_t psize); + + /* ----------------------- Internal support for realloc, memalign, etc --- */ + SPP_IMPL mchunkptr try_realloc_chunk(mchunkptr p, size_t nb, int can_move); + SPP_IMPL void* internal_memalign(size_t alignment, size_t bytes); + SPP_IMPL void** ialloc(size_t n_elements, size_t* sizes, int opts, void* chunks[]); + SPP_IMPL size_t internal_bulk_free(void* array[], size_t nelem); + SPP_IMPL void internal_inspect_all(void(*handler)(void *start, void *end, + size_t used_bytes, void* callback_arg), + void* arg); + + /* -------------------------- system alloc setup (Operations on mflags) ----- */ + bool use_lock() const { return false; } + void enable_lock() {} + void set_lock(int) {} + void disable_lock() {} + + bool use_mmap() const { return !!(_mflags & USE_MMAP_BIT); } + void enable_mmap() { _mflags |= USE_MMAP_BIT; } + +#if SPP_HAVE_MMAP + void disable_mmap() { _mflags &= ~USE_MMAP_BIT; } +#else + void disable_mmap() {} +#endif + + /* ----------------------- Runtime Check Support ------------------------- */ + + /* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When SPP_FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probabalistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. + */ + + +#if !SPP_INSECURE + // Check if address a is at least as high as any from MORECORE or MMAP + bool ok_address(void *a) const { return (char *)a >= _least_addr; } + + // Check if address of next chunk n is higher than base chunk p + static bool ok_next(void *p, void *n) { return p < n; } + + // Check if p has inuse status + static bool ok_inuse(mchunkptr p) { return p->is_inuse(); } + + // Check if p has its pinuse bit on + static bool ok_pinuse(mchunkptr p) { return p->pinuse(); } + + // Check if (alleged) mstate m has expected magic field + bool ok_magic() const { return _magic == mparams._magic; } + + // In gcc, use __builtin_expect to minimize impact of checks + #if defined(__GNUC__) && __GNUC__ >= 3 + static bool rtcheck(bool e) { return __builtin_expect(e, 1); } + #else + static bool rtcheck(bool e) { return e; } + #endif +#else + static bool ok_address(void *) { return true; } + static bool ok_next(void *, void *) { return true; } + static bool ok_inuse(mchunkptr) { return true; } + static bool ok_pinuse(mchunkptr) { return true; } + static bool ok_magic() { return true; } + static bool rtcheck(bool) { return true; } +#endif + + bool is_initialized() const { return _top != 0; } + + bool use_noncontiguous() const { return !!(_mflags & USE_NONCONTIGUOUS_BIT); } + void disable_contiguous() { _mflags |= USE_NONCONTIGUOUS_BIT; } + + // Return segment holding given address + msegmentptr segment_holding(char* addr) const + { + msegmentptr sp = (msegmentptr)&_seg; + for (;;) + { + if (addr >= sp->_base && addr < sp->_base + sp->_size) + return sp; + if ((sp = sp->_next) == 0) + return 0; + } + } + + // Return true if segment contains a segment link + int has_segment_link(msegmentptr ss) const + { + msegmentptr sp = (msegmentptr)&_seg; + for (;;) + { + if ((char*)sp >= ss->_base && (char*)sp < ss->_base + ss->_size) + return 1; + if ((sp = sp->_next) == 0) + return 0; + } + } + + bool should_trim(size_t s) const { return s > _trim_check; } + + /* -------------------------- Debugging setup ---------------------------- */ + +#if ! SPP_DEBUG + void check_free_chunk(mchunkptr) {} + void check_inuse_chunk(mchunkptr) {} + void check_malloced_chunk(void*, size_t) {} + void check_mmapped_chunk(mchunkptr) {} + void check_malloc_state() {} + void check_top_chunk(mchunkptr) {} +#else /* SPP_DEBUG */ + void check_free_chunk(mchunkptr p) { do_check_free_chunk(p); } + void check_inuse_chunk(mchunkptr p) { do_check_inuse_chunk(p); } + void check_malloced_chunk(void* p, size_t s) { do_check_malloced_chunk(p, s); } + void check_mmapped_chunk(mchunkptr p) { do_check_mmapped_chunk(p); } + void check_malloc_state() { do_check_malloc_state(); } + void check_top_chunk(mchunkptr p) { do_check_top_chunk(p); } + + void do_check_any_chunk(mchunkptr p) const; + void do_check_top_chunk(mchunkptr p) const; + void do_check_mmapped_chunk(mchunkptr p) const; + void do_check_inuse_chunk(mchunkptr p) const; + void do_check_free_chunk(mchunkptr p) const; + void do_check_malloced_chunk(void* mem, size_t s) const; + void do_check_tree(tchunkptr t); + void do_check_treebin(bindex_t i); + void do_check_smallbin(bindex_t i); + void do_check_malloc_state(); + int bin_find(mchunkptr x); + size_t traverse_and_check(); +#endif + +private: + + /* ---------------------------- Indexing Bins ---------------------------- */ + + static bool is_small(size_t s) { return (s >> SMALLBIN_SHIFT) < NSMALLBINS; } + static bindex_t small_index(size_t s) { return (bindex_t)(s >> SMALLBIN_SHIFT); } + static size_t small_index2size(size_t i) { return i << SMALLBIN_SHIFT; } + static bindex_t MIN_SMALL_INDEX() { return small_index(MIN_CHUNK_SIZE); } + + // assign tree index for size S to variable I. Use x86 asm if possible +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S) + { + unsigned int X = S >> TREEBIN_SHIFT; + if (X == 0) + return 0; + else if (X > 0xFFFF) + return NTREEBINS - 1; + + unsigned int K = (unsigned) sizeof(X) * __CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); + return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); + } + +#elif defined (__INTEL_COMPILER) + SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S) + { + size_t X = S >> TREEBIN_SHIFT; + if (X == 0) + return 0; + else if (X > 0xFFFF) + return NTREEBINS - 1; + + unsigned int K = _bit_scan_reverse(X); + return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); + } + +#elif defined(_MSC_VER) && _MSC_VER>=1300 + SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S) + { + size_t X = S >> TREEBIN_SHIFT; + if (X == 0) + return 0; + else if (X > 0xFFFF) + return NTREEBINS - 1; + + unsigned int K; + _BitScanReverse((DWORD *) &K, (DWORD) X); + return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); + } + +#else // GNUC + SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S) + { + size_t X = S >> TREEBIN_SHIFT; + if (X == 0) + return 0; + else if (X > 0xFFFF) + return NTREEBINS - 1; + + unsigned int Y = (unsigned int)X; + unsigned int N = ((Y - 0x100) >> 16) & 8; + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4; + N += K; + N += K = (((Y <<= K) - 0x4000) >> 16) & 2; + K = 14 - N + ((Y <<= K) >> 15); + return (K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)); + } +#endif + + // Shift placing maximum resolved bit in a treebin at i as sign bit + static bindex_t leftshift_for_tree_index(bindex_t i) + { + return (i == NTREEBINS - 1) ? 0 : + ((spp_size_t_bitsize - 1) - ((i >> 1) + TREEBIN_SHIFT - 2)); + } + + // The size of the smallest chunk held in bin with index i + static bindex_t minsize_for_tree_index(bindex_t i) + { + return ((size_t)1 << ((i >> 1) + TREEBIN_SHIFT)) | + (((size_t)(i & 1)) << ((i >> 1) + TREEBIN_SHIFT - 1)); + } + + + // ----------- isolate the least set bit of a bitmap + static binmap_t least_bit(binmap_t x) { return x & -x; } + + // ----------- mask with all bits to left of least bit of x on + static binmap_t left_bits(binmap_t x) { return (x << 1) | -(x << 1); } + + // index corresponding to given bit. Use x86 asm if possible +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + static bindex_t compute_bit2idx(binmap_t X) + { + unsigned int J; + J = __builtin_ctz(X); + return (bindex_t)J; + } + +#elif defined (__INTEL_COMPILER) + static bindex_t compute_bit2idx(binmap_t X) + { + unsigned int J; + J = _bit_scan_forward(X); + return (bindex_t)J; + } + +#elif defined(_MSC_VER) && _MSC_VER>=1300 + static bindex_t compute_bit2idx(binmap_t X) + { + unsigned int J; + _BitScanForward((DWORD *) &J, X); + return (bindex_t)J; + } + +#elif SPP_USE_BUILTIN_FFS + static bindex_t compute_bit2idx(binmap_t X) { return ffs(X) - 1; } + +#else + static bindex_t compute_bit2idx(binmap_t X) + { + unsigned int Y = X - 1; + unsigned int K = Y >> (16 - 4) & 16; + unsigned int N = K; Y >>= K; + N += K = Y >> (8 - 3) & 8; Y >>= K; + N += K = Y >> (4 - 2) & 4; Y >>= K; + N += K = Y >> (2 - 1) & 2; Y >>= K; + N += K = Y >> (1 - 0) & 1; Y >>= K; + return (bindex_t)(N + Y); + } +#endif + + /* ------------------------ Set up inuse chunks with or without footers ---*/ +#if !SPP_FOOTERS + void mark_inuse_foot(malloc_chunk_header *, size_t) {} +#else + //Set foot of inuse chunk to be xor of mstate and seed + void mark_inuse_foot(malloc_chunk_header *p, size_t s) + { + (((mchunkptr)((char*)p + s))->prev_foot = (size_t)this ^ mparams._magic); + } +#endif + + void set_inuse(malloc_chunk_header *p, size_t s) + { + p->_head = (p->_head & PINUSE_BIT) | s | CINUSE_BIT; + ((mchunkptr)(((char*)p) + s))->_head |= PINUSE_BIT; + mark_inuse_foot(p, s); + } + + void set_inuse_and_pinuse(malloc_chunk_header *p, size_t s) + { + p->_head = s | PINUSE_BIT | CINUSE_BIT; + ((mchunkptr)(((char*)p) + s))->_head |= PINUSE_BIT; + mark_inuse_foot(p, s); + } + + void set_size_and_pinuse_of_inuse_chunk(malloc_chunk_header *p, size_t s) + { + p->_head = s | PINUSE_BIT | CINUSE_BIT; + mark_inuse_foot(p, s); + } + + /* ------------------------ Addressing by index. See about smallbin repositioning --- */ + sbinptr smallbin_at(bindex_t i) const { return (sbinptr)((char*)&_smallbins[i << 1]); } + tbinptr* treebin_at(bindex_t i) { return &_treebins[i]; } + + /* ----------------------- bit corresponding to given index ---------*/ + static binmap_t idx2bit(bindex_t i) { return ((binmap_t)1 << i); } + + // --------------- Mark/Clear bits with given index + void mark_smallmap(bindex_t i) { _smallmap |= idx2bit(i); } + void clear_smallmap(bindex_t i) { _smallmap &= ~idx2bit(i); } + binmap_t smallmap_is_marked(bindex_t i) const { return _smallmap & idx2bit(i); } + + void mark_treemap(bindex_t i) { _treemap |= idx2bit(i); } + void clear_treemap(bindex_t i) { _treemap &= ~idx2bit(i); } + binmap_t treemap_is_marked(bindex_t i) const { return _treemap & idx2bit(i); } + + /* ------------------------ ----------------------- */ + SPP_FORCEINLINE void insert_small_chunk(mchunkptr P, size_t S); + SPP_FORCEINLINE void unlink_small_chunk(mchunkptr P, size_t S); + SPP_FORCEINLINE void unlink_first_small_chunk(mchunkptr B, mchunkptr P, bindex_t I); + SPP_FORCEINLINE void replace_dv(mchunkptr P, size_t S); + + /* ------------------------- Operations on trees ------------------------- */ + SPP_FORCEINLINE void insert_large_chunk(tchunkptr X, size_t S); + SPP_FORCEINLINE void unlink_large_chunk(tchunkptr X); + + /* ------------------------ Relays to large vs small bin operations */ + SPP_FORCEINLINE void insert_chunk(mchunkptr P, size_t S); + SPP_FORCEINLINE void unlink_chunk(mchunkptr P, size_t S); + + /* ----------------------- Direct-mmapping chunks ----------------------- */ + SPP_IMPL void* mmap_alloc(size_t nb); + SPP_IMPL mchunkptr mmap_resize(mchunkptr oldp, size_t nb, int flags); + + SPP_IMPL void reset_on_error(); + SPP_IMPL void* prepend_alloc(char* newbase, char* oldbase, size_t nb); + SPP_IMPL void add_segment(char* tbase, size_t tsize, flag_t mmapped); + + /* ------------------------ malloc --------------------------- */ + SPP_IMPL void* tmalloc_large(size_t nb); + SPP_IMPL void* tmalloc_small(size_t nb); + + /* ------------------------Bin types, widths and sizes -------- */ + static const size_t NSMALLBINS = 32; + static const size_t NTREEBINS = 32; + static const size_t SMALLBIN_SHIFT = 3; + static const size_t SMALLBIN_WIDTH = 1 << SMALLBIN_SHIFT; + static const size_t TREEBIN_SHIFT = 8; + static const size_t MIN_LARGE_SIZE = 1 << TREEBIN_SHIFT; + static const size_t MAX_SMALL_SIZE = (MIN_LARGE_SIZE - 1); + static const size_t MAX_SMALL_REQUEST = (MAX_SMALL_SIZE - spp_chunk_align_mask - CHUNK_OVERHEAD); + + /* ------------------------ data members --------------------------- */ + binmap_t _smallmap; + binmap_t _treemap; + size_t _dvsize; + size_t _topsize; + char* _least_addr; + mchunkptr _dv; + mchunkptr _top; + size_t _trim_check; + size_t _release_checks; + size_t _magic; + mchunkptr _smallbins[(NSMALLBINS + 1) * 2]; + tbinptr _treebins[NTREEBINS]; +public: + size_t _footprint; + size_t _max_footprint; + size_t _footprint_limit; // zero means no limit + flag_t _mflags; + + msegment _seg; + +private: + void* _extp; // Unused but available for extensions + size_t _exts; +}; + +typedef malloc_state* mstate; + +/* ------------- end malloc_state ------------------- */ + +#if SPP_FOOTERS +static malloc_state* get_mstate_for(malloc_chunk_header *p) +{ + return (malloc_state*)(((mchunkptr)((char*)(p) + + (p->chunksize())))->prev_foot ^ mparams._magic); +} +#endif + +/* -------------------------- system alloc setup ------------------------- */ + + + +// For mmap, use granularity alignment on windows, else page-align +#ifdef WIN32 + #define mmap_align(S) mparams.granularity_align(S) +#else + #define mmap_align(S) mparams.page_align(S) +#endif + +// True if segment S holds address A +static bool segment_holds(msegmentptr S, mchunkptr A) +{ + return (char*)A >= S->_base && (char*)A < S->_base + S->_size; +} + +/* + top_foot_size is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +static SPP_FORCEINLINE size_t top_foot_size() +{ + return align_offset(chunk2mem((void *)0)) + + pad_request(sizeof(struct malloc_segment)) + + MIN_CHUNK_SIZE; +} + + +// For sys_alloc, enough padding to ensure can malloc request on success +static SPP_FORCEINLINE size_t sys_alloc_padding() +{ + return top_foot_size() + SPP_MALLOC_ALIGNMENT; +} + + +#define SPP_USAGE_ERROR_ACTION(m,p) SPP_ABORT + +/* ---------------------------- setting mparams -------------------------- */ + +// Initialize mparams +int malloc_params::_init() +{ +#ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); +#endif + + if (_magic == 0) + { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef WIN32 + psize = malloc_getpagesize; + gsize = ((SPP_DEFAULT_GRANULARITY != 0) ? SPP_DEFAULT_GRANULARITY : psize); +#else + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((SPP_DEFAULT_GRANULARITY != 0) ? + SPP_DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (spp_max_size_t < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (SPP_MALLOC_ALIGNMENT < (size_t)8U) || + ((SPP_MALLOC_ALIGNMENT & (SPP_MALLOC_ALIGNMENT - 1)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE - 1)) != 0) || + ((gsize & (gsize - 1)) != 0) || + ((psize & (psize - 1)) != 0)) + SPP_ABORT; + _granularity = gsize; + _page_size = psize; + _mmap_threshold = SPP_DEFAULT_MMAP_THRESHOLD; + _trim_threshold = SPP_DEFAULT_TRIM_THRESHOLD; + _default_mflags = USE_MMAP_BIT | USE_NONCONTIGUOUS_BIT; + + { +#if SPP_USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + // Try to use /dev/urandom, else fall back on using time + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) + { + magic = *((size_t *) buf); + close(fd); + } + else +#endif + { +#ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#elif defined(SPP_LACKS_TIME_H) + magic = (size_t)&magic ^ (size_t)0x55555555U; +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + } + magic |= (size_t)8U; // ensure nonzero + magic &= ~(size_t)7U; // improve chances of fault for bad values + // Until memory modes commonly available, use volatile-write + (*(volatile size_t *)(&(_magic))) = magic; + } + } + + return 1; +} + +/* + mallopt tuning options. SVID/XPG defines four standard parameter + numbers for mallopt, normally defined in malloc.h. None of these + are used in this malloc, so setting them has no effect. But this + malloc does support the following options. +*/ +static const int m_trim_threshold = -1; +static const int m_granularity = -2; +static const int m_mmap_threshold = -3; + +// support for mallopt +int malloc_params::change(int param_number, int value) +{ + size_t val; + ensure_initialization(); + val = (value == -1) ? spp_max_size_t : (size_t)value; + + switch (param_number) + { + case m_trim_threshold: + _trim_threshold = val; + return 1; + + case m_granularity: + if (val >= _page_size && ((val & (val - 1)) == 0)) + { + _granularity = val; + return 1; + } + else + return 0; + + case m_mmap_threshold: + _mmap_threshold = val; + return 1; + + default: + return 0; + } +} + +#if SPP_DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +// Check properties of any chunk, whether free, inuse, mmapped etc +void malloc_state::do_check_any_chunk(mchunkptr p) const +{ + assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD)); + assert(ok_address(p)); +} + +// Check properties of top chunk +void malloc_state::do_check_top_chunk(mchunkptr p) const +{ + msegmentptr sp = segment_holding((char*)p); + size_t sz = p->_head & ~INUSE_BITS; // third-lowest bit can be set! + assert(sp != 0); + assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD)); + assert(ok_address(p)); + assert(sz == _topsize); + assert(sz > 0); + assert(sz == ((sp->_base + sp->_size) - (char*)p) - top_foot_size()); + assert(p->pinuse()); + assert(!p->chunk_plus_offset(sz)->pinuse()); +} + +// Check properties of (inuse) mmapped chunks +void malloc_state::do_check_mmapped_chunk(mchunkptr p) const +{ + size_t sz = p->chunksize(); + size_t len = (sz + (p->_prev_foot) + SPP_MMAP_FOOT_PAD); + assert(p->is_mmapped()); + assert(use_mmap()); + assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD)); + assert(ok_address(p)); + assert(!is_small(sz)); + assert((len & (mparams._page_size - 1)) == 0); + assert(p->chunk_plus_offset(sz)->_head == FENCEPOST_HEAD); + assert(p->chunk_plus_offset(sz + sizeof(size_t))->_head == 0); +} + +// Check properties of inuse chunks +void malloc_state::do_check_inuse_chunk(mchunkptr p) const +{ + do_check_any_chunk(p); + assert(p->is_inuse()); + assert(p->next_pinuse()); + // If not pinuse and not mmapped, previous chunk has OK offset + assert(p->is_mmapped() || p->pinuse() || (mchunkptr)p->prev_chunk()->next_chunk() == p); + if (p->is_mmapped()) + do_check_mmapped_chunk(p); +} + +// Check properties of free chunks +void malloc_state::do_check_free_chunk(mchunkptr p) const +{ + size_t sz = p->chunksize(); + mchunkptr next = (mchunkptr)p->chunk_plus_offset(sz); + do_check_any_chunk(p); + assert(!p->is_inuse()); + assert(!p->next_pinuse()); + assert(!p->is_mmapped()); + if (p != _dv && p != _top) + { + if (sz >= MIN_CHUNK_SIZE) + { + assert((sz & spp_chunk_align_mask) == 0); + assert(spp_is_aligned(chunk2mem(p))); + assert(next->_prev_foot == sz); + assert(p->pinuse()); + assert(next == _top || next->is_inuse()); + assert(p->_fd->_bk == p); + assert(p->_bk->_fd == p); + } + else // markers are always of size sizeof(size_t) + assert(sz == sizeof(size_t)); + } +} + +// Check properties of malloced chunks at the point they are malloced +void malloc_state::do_check_malloced_chunk(void* mem, size_t s) const +{ + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); + size_t sz = p->_head & ~INUSE_BITS; + do_check_inuse_chunk(p); + assert((sz & spp_chunk_align_mask) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + // unless mmapped, size is less than MIN_CHUNK_SIZE more than request + assert(p->is_mmapped() || sz < (s + MIN_CHUNK_SIZE)); + } +} + +// Check a tree and its subtrees. +void malloc_state::do_check_tree(tchunkptr t) +{ + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->_index; + size_t tsize = t->chunksize(); + bindex_t idx = compute_tree_index(tsize); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS - 1) || (tsize < minsize_for_tree_index((idx + 1)))); + + do + { + // traverse through chain of same-sized nodes + do_check_any_chunk((mchunkptr)u); + assert(u->_index == tindex); + assert(u->chunksize() == tsize); + assert(!u->is_inuse()); + assert(!u->next_pinuse()); + assert(u->_fd->_bk == u); + assert(u->_bk->_fd == u); + if (u->_parent == 0) + { + assert(u->_child[0] == 0); + assert(u->_child[1] == 0); + } + else + { + assert(head == 0); // only one node on chain has parent + head = u; + assert(u->_parent != u); + assert(u->_parent->_child[0] == u || + u->_parent->_child[1] == u || + *((tbinptr*)(u->_parent)) == u); + if (u->_child[0] != 0) + { + assert(u->_child[0]->_parent == u); + assert(u->_child[0] != u); + do_check_tree(u->_child[0]); + } + if (u->_child[1] != 0) + { + assert(u->_child[1]->_parent == u); + assert(u->_child[1] != u); + do_check_tree(u->_child[1]); + } + if (u->_child[0] != 0 && u->_child[1] != 0) + assert(u->_child[0]->chunksize() < u->_child[1]->chunksize()); + } + u = u->_fd; + } + while (u != t); + assert(head != 0); +} + +// Check all the chunks in a treebin. +void malloc_state::do_check_treebin(bindex_t i) +{ + tbinptr* tb = (tbinptr*)treebin_at(i); + tchunkptr t = *tb; + int empty = (_treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(t); +} + +// Check all the chunks in a smallbin. +void malloc_state::do_check_smallbin(bindex_t i) +{ + sbinptr b = smallbin_at(i); + mchunkptr p = b->_bk; + unsigned int empty = (_smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) + { + for (; p != b; p = p->_bk) + { + size_t size = p->chunksize(); + mchunkptr q; + // each chunk claims to be free + do_check_free_chunk(p); + // chunk belongs in bin + assert(small_index(size) == i); + assert(p->_bk == b || p->_bk->chunksize() == p->chunksize()); + // chunk is followed by an inuse chunk + q = (mchunkptr)p->next_chunk(); + if (q->_head != FENCEPOST_HEAD) + do_check_inuse_chunk(q); + } + } +} + +// Find x in a bin. Used in other check functions. +int malloc_state::bin_find(mchunkptr x) +{ + size_t size = x->chunksize(); + if (is_small(size)) + { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(sidx); + if (smallmap_is_marked(sidx)) + { + mchunkptr p = b; + do + { + if (p == x) + return 1; + } + while ((p = p->_fd) != b); + } + } + else + { + bindex_t tidx = compute_tree_index(size); + if (treemap_is_marked(tidx)) + { + tchunkptr t = *treebin_at(tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && t->chunksize() != size) + { + t = t->_child[(sizebits >> (spp_size_t_bitsize - 1)) & 1]; + sizebits <<= 1; + } + if (t != 0) + { + tchunkptr u = t; + do + { + if (u == (tchunkptr)x) + return 1; + } + while ((u = u->_fd) != t); + } + } + } + return 0; +} + +// Traverse each chunk and check it; return total +size_t malloc_state::traverse_and_check() +{ + size_t sum = 0; + if (is_initialized()) + { + msegmentptr s = (msegmentptr)&_seg; + sum += _topsize + top_foot_size(); + while (s != 0) + { + mchunkptr q = align_as_chunk(s->_base); + mchunkptr lastq = 0; + assert(q->pinuse()); + while (segment_holds(s, q) && + q != _top && q->_head != FENCEPOST_HEAD) + { + sum += q->chunksize(); + if (q->is_inuse()) + { + assert(!bin_find(q)); + do_check_inuse_chunk(q); + } + else + { + assert(q == _dv || bin_find(q)); + assert(lastq == 0 || lastq->is_inuse()); // Not 2 consecutive free + do_check_free_chunk(q); + } + lastq = q; + q = (mchunkptr)q->next_chunk(); + } + s = s->_next; + } + } + return sum; +} + + +// Check all properties of malloc_state. +void malloc_state::do_check_malloc_state() +{ + bindex_t i; + size_t total; + // check bins + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(i); + + if (_dvsize != 0) + { + // check dv chunk + do_check_any_chunk(_dv); + assert(_dvsize == _dv->chunksize()); + assert(_dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(_dv) == 0); + } + + if (_top != 0) + { + // check top chunk + do_check_top_chunk(_top); + //assert(topsize == top->chunksize()); redundant + assert(_topsize > 0); + assert(bin_find(_top) == 0); + } + + total = traverse_and_check(); + assert(total <= _footprint); + assert(_footprint <= _max_footprint); +} +#endif // SPP_DEBUG + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +// Link a free chunk into a smallbin +void malloc_state::insert_small_chunk(mchunkptr p, size_t s) +{ + bindex_t I = small_index(s); + mchunkptr B = smallbin_at(I); + mchunkptr F = B; + assert(s >= MIN_CHUNK_SIZE); + if (!smallmap_is_marked(I)) + mark_smallmap(I); + else if (rtcheck(ok_address(B->_fd))) + F = B->_fd; + else + SPP_ABORT; + B->_fd = p; + F->_bk = p; + p->_fd = F; + p->_bk = B; +} + +// Unlink a chunk from a smallbin +void malloc_state::unlink_small_chunk(mchunkptr p, size_t s) +{ + mchunkptr F = p->_fd; + mchunkptr B = p->_bk; + bindex_t I = small_index(s); + assert(p != B); + assert(p != F); + assert(p->chunksize() == small_index2size(I)); + if (rtcheck(F == smallbin_at(I) || (ok_address(F) && F->_bk == p))) + { + if (B == F) + clear_smallmap(I); + else if (rtcheck(B == smallbin_at(I) || + (ok_address(B) && B->_fd == p))) + { + F->_bk = B; + B->_fd = F; + } + else + SPP_ABORT; + } + else + SPP_ABORT; +} + +// Unlink the first chunk from a smallbin +void malloc_state::unlink_first_small_chunk(mchunkptr B, mchunkptr p, bindex_t I) +{ + mchunkptr F = p->_fd; + assert(p != B); + assert(p != F); + assert(p->chunksize() == small_index2size(I)); + if (B == F) + clear_smallmap(I); + else if (rtcheck(ok_address(F) && F->_bk == p)) + { + F->_bk = B; + B->_fd = F; + } + else + SPP_ABORT; +} + +// Replace dv node, binning the old one +// Used only when dvsize known to be small +void malloc_state::replace_dv(mchunkptr p, size_t s) +{ + size_t DVS = _dvsize; + assert(is_small(DVS)); + if (DVS != 0) + { + mchunkptr DV = _dv; + insert_small_chunk(DV, DVS); + } + _dvsize = s; + _dv = p; +} + +/* ------------------------- Operations on trees ------------------------- */ + +// Insert chunk into tree +void malloc_state::insert_large_chunk(tchunkptr X, size_t s) +{ + tbinptr* H; + bindex_t I = compute_tree_index(s); + H = treebin_at(I); + X->_index = I; + X->_child[0] = X->_child[1] = 0; + if (!treemap_is_marked(I)) + { + mark_treemap(I); + *H = X; + X->_parent = (tchunkptr)H; + X->_fd = X->_bk = X; + } + else + { + tchunkptr T = *H; + size_t K = s << leftshift_for_tree_index(I); + for (;;) + { + if (T->chunksize() != s) + { + tchunkptr* C = &(T->_child[(K >> (spp_size_t_bitsize - 1)) & 1]); + K <<= 1; + if (*C != 0) + T = *C; + else if (rtcheck(ok_address(C))) + { + *C = X; + X->_parent = T; + X->_fd = X->_bk = X; + break; + } + else + { + SPP_ABORT; + break; + } + } + else + { + tchunkptr F = T->_fd; + if (rtcheck(ok_address(T) && ok_address(F))) + { + T->_fd = F->_bk = X; + X->_fd = F; + X->_bk = T; + X->_parent = 0; + break; + } + else + { + SPP_ABORT; + break; + } + } + } + } +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +void malloc_state::unlink_large_chunk(tchunkptr X) +{ + tchunkptr XP = X->_parent; + tchunkptr R; + if (X->_bk != X) + { + tchunkptr F = X->_fd; + R = X->_bk; + if (rtcheck(ok_address(F) && F->_bk == X && R->_fd == X)) + { + F->_bk = R; + R->_fd = F; + } + else + SPP_ABORT; + } + else + { + tchunkptr* RP; + if (((R = *(RP = &(X->_child[1]))) != 0) || + ((R = *(RP = &(X->_child[0]))) != 0)) + { + tchunkptr* CP; + while ((*(CP = &(R->_child[1])) != 0) || + (*(CP = &(R->_child[0])) != 0)) + R = *(RP = CP); + if (rtcheck(ok_address(RP))) + *RP = 0; + else + SPP_ABORT; + } + } + if (XP != 0) + { + tbinptr* H = treebin_at(X->_index); + if (X == *H) + { + if ((*H = R) == 0) + clear_treemap(X->_index); + } + else if (rtcheck(ok_address(XP))) + { + if (XP->_child[0] == X) + XP->_child[0] = R; + else + XP->_child[1] = R; + } + else + SPP_ABORT; + if (R != 0) + { + if (rtcheck(ok_address(R))) + { + tchunkptr C0, C1; + R->_parent = XP; + if ((C0 = X->_child[0]) != 0) + { + if (rtcheck(ok_address(C0))) + { + R->_child[0] = C0; + C0->_parent = R; + } + else + SPP_ABORT; + } + if ((C1 = X->_child[1]) != 0) + { + if (rtcheck(ok_address(C1))) + { + R->_child[1] = C1; + C1->_parent = R; + } + else + SPP_ABORT; + } + } + else + SPP_ABORT; + } + } +} + +// Relays to large vs small bin operations + +void malloc_state::insert_chunk(mchunkptr p, size_t s) +{ + if (is_small(s)) + insert_small_chunk(p, s); + else + { + tchunkptr tp = (tchunkptr)(p); + insert_large_chunk(tp, s); + } +} + +void malloc_state::unlink_chunk(mchunkptr p, size_t s) +{ + if (is_small(s)) + unlink_small_chunk(p, s); + else + { + tchunkptr tp = (tchunkptr)(p); + unlink_large_chunk(tp); + } +} + + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). +*/ + +// Malloc using mmap +void* malloc_state::mmap_alloc(size_t nb) +{ + size_t mmsize = mmap_align(nb + 6 * sizeof(size_t) + spp_chunk_align_mask); + if (_footprint_limit != 0) + { + size_t fp = _footprint + mmsize; + if (fp <= _footprint || fp > _footprint_limit) + return 0; + } + if (mmsize > nb) + { + // Check for wrap around 0 + char* mm = (char*)(SPP_CALL_DIRECT_MMAP(mmsize)); + if (mm != cmfail) + { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - SPP_MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->_prev_foot = offset; + p->_head = psize; + mark_inuse_foot(p, psize); + p->chunk_plus_offset(psize)->_head = FENCEPOST_HEAD; + p->chunk_plus_offset(psize + sizeof(size_t))->_head = 0; + + if (_least_addr == 0 || mm < _least_addr) + _least_addr = mm; + if ((_footprint += mmsize) > _max_footprint) + _max_footprint = _footprint; + assert(spp_is_aligned(chunk2mem(p))); + check_mmapped_chunk(p); + return chunk2mem(p); + } + } + return 0; +} + +// Realloc using mmap +mchunkptr malloc_state::mmap_resize(mchunkptr oldp, size_t nb, int flags) +{ + size_t oldsize = oldp->chunksize(); + (void)flags; // placate people compiling -Wunused + if (is_small(nb)) // Can't shrink mmap regions below small size + return 0; + + // Keep old chunk if big enough but not too big + if (oldsize >= nb + sizeof(size_t) && + (oldsize - nb) <= (mparams._granularity << 1)) + return oldp; + else + { + size_t offset = oldp->_prev_foot; + size_t oldmmsize = oldsize + offset + SPP_MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + 6 * sizeof(size_t) + spp_chunk_align_mask); + char* cp = (char*)SPP_CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, flags); + if (cp != cmfail) + { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - SPP_MMAP_FOOT_PAD; + newp->_head = psize; + mark_inuse_foot(newp, psize); + newp->chunk_plus_offset(psize)->_head = FENCEPOST_HEAD; + newp->chunk_plus_offset(psize + sizeof(size_t))->_head = 0; + + if (cp < _least_addr) + _least_addr = cp; + if ((_footprint += newmmsize - oldmmsize) > _max_footprint) + _max_footprint = _footprint; + check_mmapped_chunk(newp); + return newp; + } + } + return 0; +} + + +/* -------------------------- mspace management -------------------------- */ + +// Initialize top chunk and its size +void malloc_state::init_top(mchunkptr p, size_t psize) +{ + // Ensure alignment + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + _top = p; + _topsize = psize; + p->_head = psize | PINUSE_BIT; + // set size of fake trailing chunk holding overhead space only once + p->chunk_plus_offset(psize)->_head = top_foot_size(); + _trim_check = mparams._trim_threshold; // reset on each update +} + +// Initialize bins for a new mstate that is otherwise zeroed out +void malloc_state::init_bins() +{ + // Establish circular links for smallbins + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) + { + sbinptr bin = smallbin_at(i); + bin->_fd = bin->_bk = bin; + } +} + +#if SPP_PROCEED_ON_ERROR + +// default corruption action +void malloc_state::reset_on_error() +{ + int i; + ++malloc_corruption_error_count; + // Reinitialize fields to forget about all memory + _smallmap = _treemap = 0; + _dvsize = _topsize = 0; + _seg._base = 0; + _seg._size = 0; + _seg._next = 0; + _top = _dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(i) = 0; + init_bins(); +} +#endif + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +void* malloc_state::prepend_alloc(char* newbase, char* oldbase, size_t nb) +{ + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = (mchunkptr)p->chunk_plus_offset(nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(p, nb); + + assert((char*)oldfirst > (char*)q); + assert(oldfirst->pinuse()); + assert(qsize >= MIN_CHUNK_SIZE); + + // consolidate remainder with first chunk of old base + if (oldfirst == _top) + { + size_t tsize = _topsize += qsize; + _top = q; + q->_head = tsize | PINUSE_BIT; + check_top_chunk(q); + } + else if (oldfirst == _dv) + { + size_t dsize = _dvsize += qsize; + _dv = q; + q->set_size_and_pinuse_of_free_chunk(dsize); + } + else + { + if (!oldfirst->is_inuse()) + { + size_t nsize = oldfirst->chunksize(); + unlink_chunk(oldfirst, nsize); + oldfirst = (mchunkptr)oldfirst->chunk_plus_offset(nsize); + qsize += nsize; + } + q->set_free_with_pinuse(qsize, oldfirst); + insert_chunk(q, qsize); + check_free_chunk(q); + } + + check_malloced_chunk(chunk2mem(p), nb); + return chunk2mem(p); +} + +// Add a segment to hold a new noncontiguous region +void malloc_state::add_segment(char* tbase, size_t tsize, flag_t mmapped) +{ + // Determine locations and sizes of segment, fenceposts, old top + char* old_top = (char*)_top; + msegmentptr oldsp = segment_holding(old_top); + char* old_end = oldsp->_base + oldsp->_size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + 4 * sizeof(size_t) + spp_chunk_align_mask); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE)) ? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = (mchunkptr)sp->chunk_plus_offset(ssize); + mchunkptr p = tnext; + int nfences = 0; + + // reset top to new space + init_top((mchunkptr)tbase, tsize - top_foot_size()); + + // Set up segment record + assert(spp_is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(sp, ssize); + *ss = _seg; // Push current record + _seg._base = tbase; + _seg._size = tsize; + _seg._sflags = mmapped; + _seg._next = ss; + + // Insert trailing fenceposts + for (;;) + { + mchunkptr nextp = (mchunkptr)p->chunk_plus_offset(sizeof(size_t)); + p->_head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->_head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + // Insert the rest of old top into a bin as an ordinary free chunk + if (csp != old_top) + { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = (mchunkptr)q->chunk_plus_offset(psize); + q->set_free_with_pinuse(psize, tn); + insert_chunk(q, psize); + } + + check_top_chunk(_top); +} + +/* -------------------------- System allocation -------------------------- */ + +// Get memory from system using MMAP +void* malloc_state::sys_alloc(size_t nb) +{ + char* tbase = cmfail; + size_t tsize = 0; + flag_t mmap_flag = 0; + size_t asize; // allocation size + + mparams.ensure_initialization(); + + // Directly map large chunks, but only if already initialized + if (use_mmap() && nb >= mparams._mmap_threshold && _topsize != 0) + { + void* mem = mmap_alloc(nb); + if (mem != 0) + return mem; + } + + asize = mparams.granularity_align(nb + sys_alloc_padding()); + if (asize <= nb) + return 0; // wraparound + if (_footprint_limit != 0) + { + size_t fp = _footprint + asize; + if (fp <= _footprint || fp > _footprint_limit) + return 0; + } + + /* + Try getting memory with a call to MMAP new space (disabled if not SPP_HAVE_MMAP). + We need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (SPP_HAVE_MMAP && tbase == cmfail) + { + // Try MMAP + char* mp = (char*)(SPP_CALL_MMAP(asize)); + if (mp != cmfail) + { + tbase = mp; + tsize = asize; + mmap_flag = USE_MMAP_BIT; + } + } + + if (tbase != cmfail) + { + + if ((_footprint += tsize) > _max_footprint) + _max_footprint = _footprint; + + if (!is_initialized()) + { + // first-time initialization + if (_least_addr == 0 || tbase < _least_addr) + _least_addr = tbase; + _seg._base = tbase; + _seg._size = tsize; + _seg._sflags = mmap_flag; + _magic = mparams._magic; + _release_checks = SPP_MAX_RELEASE_CHECK_RATE; + init_bins(); + + // Offset top by embedded malloc_state + mchunkptr mn = (mchunkptr)mem2chunk(this)->next_chunk(); + init_top(mn, (size_t)((tbase + tsize) - (char*)mn) - top_foot_size()); + } + + else + { + // Try to merge with an existing segment + msegmentptr sp = &_seg; + // Only consider most recent segment if traversal suppressed + while (sp != 0 && tbase != sp->_base + sp->_size) + sp = (SPP_NO_SEGMENT_TRAVERSAL) ? 0 : sp->_next; + if (sp != 0 && + !sp->is_extern_segment() && + (sp->_sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, _top)) + { + // append + sp->_size += tsize; + init_top(_top, _topsize + tsize); + } + else + { + if (tbase < _least_addr) + _least_addr = tbase; + sp = &_seg; + while (sp != 0 && sp->_base != tbase + tsize) + sp = (SPP_NO_SEGMENT_TRAVERSAL) ? 0 : sp->_next; + if (sp != 0 && + !sp->is_extern_segment() && + (sp->_sflags & USE_MMAP_BIT) == mmap_flag) + { + char* oldbase = sp->_base; + sp->_base = tbase; + sp->_size += tsize; + return prepend_alloc(tbase, oldbase, nb); + } + else + add_segment(tbase, tsize, mmap_flag); + } + } + + if (nb < _topsize) + { + // Allocate from new or extended top space + size_t rsize = _topsize -= nb; + mchunkptr p = _top; + mchunkptr r = _top = (mchunkptr)p->chunk_plus_offset(nb); + r->_head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(p, nb); + check_top_chunk(_top); + check_malloced_chunk(chunk2mem(p), nb); + return chunk2mem(p); + } + } + + SPP_MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +// Unmap and unlink any mmapped segments that don't contain used chunks +size_t malloc_state::release_unused_segments() +{ + size_t released = 0; + int nsegs = 0; + msegmentptr pred = &_seg; + msegmentptr sp = pred->_next; + while (sp != 0) + { + char* base = sp->_base; + size_t size = sp->_size; + msegmentptr next = sp->_next; + ++nsegs; + if (sp->is_mmapped_segment() && !sp->is_extern_segment()) + { + mchunkptr p = align_as_chunk(base); + size_t psize = p->chunksize(); + // Can unmap if first chunk holds entire segment and not pinned + if (!p->is_inuse() && (char*)p + psize >= base + size - top_foot_size()) + { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, p)); + if (p == _dv) + { + _dv = 0; + _dvsize = 0; + } + else + unlink_large_chunk(tp); + if (SPP_CALL_MUNMAP(base, size) == 0) + { + released += size; + _footprint -= size; + // unlink obsoleted record + sp = pred; + sp->_next = next; + } + else + { + // back out if cannot unmap + insert_large_chunk(tp, psize); + } + } + } + if (SPP_NO_SEGMENT_TRAVERSAL) // scan only first segment + break; + pred = sp; + sp = next; + } + // Reset check counter + _release_checks = (((size_t) nsegs > (size_t) SPP_MAX_RELEASE_CHECK_RATE) ? + (size_t) nsegs : (size_t) SPP_MAX_RELEASE_CHECK_RATE); + return released; +} + +int malloc_state::sys_trim(size_t pad) +{ + size_t released = 0; + mparams.ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized()) + { + pad += top_foot_size(); // ensure enough room for segment overhead + + if (_topsize > pad) + { + // Shrink top space in _granularity - size units, keeping at least one + size_t unit = mparams._granularity; + size_t extra = ((_topsize - pad + (unit - 1)) / unit - + 1) * unit; + msegmentptr sp = segment_holding((char*)_top); + + if (!sp->is_extern_segment()) + { + if (sp->is_mmapped_segment()) + { + if (SPP_HAVE_MMAP && + sp->_size >= extra && + !has_segment_link(sp)) + { + // can't shrink if pinned + size_t newsize = sp->_size - extra; + (void)newsize; // placate people compiling -Wunused-variable + // Prefer mremap, fall back to munmap + if ((SPP_CALL_MREMAP(sp->_base, sp->_size, newsize, 0) != mfail) || + (SPP_CALL_MUNMAP(sp->_base + newsize, extra) == 0)) + released = extra; + } + } + } + + if (released != 0) + { + sp->_size -= released; + _footprint -= released; + init_top(_top, _topsize - released); + check_top_chunk(_top); + } + } + + // Unmap any unused mmapped segments + if (SPP_HAVE_MMAP) + released += release_unused_segments(); + + // On failure, disable autotrim to avoid repeated failed future calls + if (released == 0 && _topsize > _trim_check) + _trim_check = spp_max_size_t; + } + + return (released != 0) ? 1 : 0; +} + +/* Consolidate and bin a chunk. Differs from exported versions + of free mainly in that the chunk need not be marked as inuse. +*/ +void malloc_state::dispose_chunk(mchunkptr p, size_t psize) +{ + mchunkptr next = (mchunkptr)p->chunk_plus_offset(psize); + if (!p->pinuse()) + { + mchunkptr prev; + size_t prevsize = p->_prev_foot; + if (p->is_mmapped()) + { + psize += prevsize + SPP_MMAP_FOOT_PAD; + if (SPP_CALL_MUNMAP((char*)p - prevsize, psize) == 0) + _footprint -= psize; + return; + } + prev = (mchunkptr)p->chunk_minus_offset(prevsize); + psize += prevsize; + p = prev; + if (rtcheck(ok_address(prev))) + { + // consolidate backward + if (p != _dv) + unlink_chunk(p, prevsize); + else if ((next->_head & INUSE_BITS) == INUSE_BITS) + { + _dvsize = psize; + p->set_free_with_pinuse(psize, next); + return; + } + } + else + { + SPP_ABORT; + return; + } + } + if (rtcheck(ok_address(next))) + { + if (!next->cinuse()) + { + // consolidate forward + if (next == _top) + { + size_t tsize = _topsize += psize; + _top = p; + p->_head = tsize | PINUSE_BIT; + if (p == _dv) + { + _dv = 0; + _dvsize = 0; + } + return; + } + else if (next == _dv) + { + size_t dsize = _dvsize += psize; + _dv = p; + p->set_size_and_pinuse_of_free_chunk(dsize); + return; + } + else + { + size_t nsize = next->chunksize(); + psize += nsize; + unlink_chunk(next, nsize); + p->set_size_and_pinuse_of_free_chunk(psize); + if (p == _dv) + { + _dvsize = psize; + return; + } + } + } + else + p->set_free_with_pinuse(psize, next); + insert_chunk(p, psize); + } + else + SPP_ABORT; +} + +/* ---------------------------- malloc --------------------------- */ + +// allocate a large request from the best fitting chunk in a treebin +void* malloc_state::tmalloc_large(size_t nb) +{ + tchunkptr v = 0; + size_t rsize = -nb; // Unsigned negation + tchunkptr t; + bindex_t idx = compute_tree_index(nb); + if ((t = *treebin_at(idx)) != 0) + { + // Traverse tree for this bin looking for node with size == nb + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; // The deepest untaken right subtree + for (;;) + { + tchunkptr rt; + size_t trem = t->chunksize() - nb; + if (trem < rsize) + { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->_child[1]; + t = t->_child[(sizebits >> (spp_size_t_bitsize - 1)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) + { + t = rst; // set t to least subtree holding sizes > nb + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) + { + // set t to root of next non-empty treebin + binmap_t leftbits = left_bits(idx2bit(idx)) & _treemap; + if (leftbits != 0) + { + binmap_t leastbit = least_bit(leftbits); + bindex_t i = compute_bit2idx(leastbit); + t = *treebin_at(i); + } + } + + while (t != 0) + { + // find smallest of tree or subtree + size_t trem = t->chunksize() - nb; + if (trem < rsize) + { + rsize = trem; + v = t; + } + t = t->leftmost_child(); + } + + // If dv is a better fit, return 0 so malloc will use it + if (v != 0 && rsize < (size_t)(_dvsize - nb)) + { + if (rtcheck(ok_address(v))) + { + // split + mchunkptr r = (mchunkptr)v->chunk_plus_offset(nb); + assert(v->chunksize() == rsize + nb); + if (rtcheck(ok_next(v, r))) + { + unlink_large_chunk(v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(v, (rsize + nb)); + else + { + set_size_and_pinuse_of_inuse_chunk(v, nb); + r->set_size_and_pinuse_of_free_chunk(rsize); + insert_chunk(r, rsize); + } + return chunk2mem(v); + } + } + SPP_ABORT; + } + return 0; +} + +// allocate a small request from the best fitting chunk in a treebin +void* malloc_state::tmalloc_small(size_t nb) +{ + tchunkptr t, v; + size_t rsize; + binmap_t leastbit = least_bit(_treemap); + bindex_t i = compute_bit2idx(leastbit); + v = t = *treebin_at(i); + rsize = t->chunksize() - nb; + + while ((t = t->leftmost_child()) != 0) + { + size_t trem = t->chunksize() - nb; + if (trem < rsize) + { + rsize = trem; + v = t; + } + } + + if (rtcheck(ok_address(v))) + { + mchunkptr r = (mchunkptr)v->chunk_plus_offset(nb); + assert(v->chunksize() == rsize + nb); + if (rtcheck(ok_next(v, r))) + { + unlink_large_chunk(v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(v, (rsize + nb)); + else + { + set_size_and_pinuse_of_inuse_chunk(v, nb); + r->set_size_and_pinuse_of_free_chunk(rsize); + replace_dv(r, rsize); + } + return chunk2mem(v); + } + } + + SPP_ABORT; + return 0; +} + +/* ---------------------------- malloc --------------------------- */ + +void* malloc_state::_malloc(size_t bytes) +{ + if (1) + { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) + { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = _smallmap >> idx; + + if ((smallbits & 0x3U) != 0) + { + // Remainderless fit to a smallbin. + mchunkptr b, p; + idx += ~smallbits & 1; // Uses next bin if idx empty + b = smallbin_at(idx); + p = b->_fd; + assert(p->chunksize() == small_index2size(idx)); + unlink_first_small_chunk(b, p, idx); + set_inuse_and_pinuse(p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(mem, nb); + goto postaction; + } + + else if (nb > _dvsize) + { + if (smallbits != 0) + { + // Use chunk in next nonempty smallbin + mchunkptr b, p, r; + size_t rsize; + binmap_t leftbits = (smallbits << idx) & left_bits(malloc_state::idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + bindex_t i = compute_bit2idx(leastbit); + b = smallbin_at(i); + p = b->_fd; + assert(p->chunksize() == small_index2size(i)); + unlink_first_small_chunk(b, p, i); + rsize = small_index2size(i) - nb; + // Fit here cannot be remainderless if 4byte sizes + if (sizeof(size_t) != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(p, small_index2size(i)); + else + { + set_size_and_pinuse_of_inuse_chunk(p, nb); + r = (mchunkptr)p->chunk_plus_offset(nb); + r->set_size_and_pinuse_of_free_chunk(rsize); + replace_dv(r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(mem, nb); + goto postaction; + } + + else if (_treemap != 0 && (mem = tmalloc_small(nb)) != 0) + { + check_malloced_chunk(mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = spp_max_size_t; // Too big to allocate. Force failure (in sys alloc) + else + { + nb = pad_request(bytes); + if (_treemap != 0 && (mem = tmalloc_large(nb)) != 0) + { + check_malloced_chunk(mem, nb); + goto postaction; + } + } + + if (nb <= _dvsize) + { + size_t rsize = _dvsize - nb; + mchunkptr p = _dv; + if (rsize >= MIN_CHUNK_SIZE) + { + // split dv + mchunkptr r = _dv = (mchunkptr)p->chunk_plus_offset(nb); + _dvsize = rsize; + r->set_size_and_pinuse_of_free_chunk(rsize); + set_size_and_pinuse_of_inuse_chunk(p, nb); + } + else // exhaust dv + { + size_t dvs = _dvsize; + _dvsize = 0; + _dv = 0; + set_inuse_and_pinuse(p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(mem, nb); + goto postaction; + } + + else if (nb < _topsize) + { + // Split top + size_t rsize = _topsize -= nb; + mchunkptr p = _top; + mchunkptr r = _top = (mchunkptr)p->chunk_plus_offset(nb); + r->_head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(p, nb); + mem = chunk2mem(p); + check_top_chunk(_top); + check_malloced_chunk(mem, nb); + goto postaction; + } + + mem = sys_alloc(nb); + +postaction: + return mem; + } + + return 0; +} + +/* ---------------------------- free --------------------------- */ + +void malloc_state::_free(mchunkptr p) +{ + if (1) + { + check_inuse_chunk(p); + if (rtcheck(ok_address(p) && ok_inuse(p))) + { + size_t psize = p->chunksize(); + mchunkptr next = (mchunkptr)p->chunk_plus_offset(psize); + if (!p->pinuse()) + { + size_t prevsize = p->_prev_foot; + if (p->is_mmapped()) + { + psize += prevsize + SPP_MMAP_FOOT_PAD; + if (SPP_CALL_MUNMAP((char*)p - prevsize, psize) == 0) + _footprint -= psize; + goto postaction; + } + else + { + mchunkptr prev = (mchunkptr)p->chunk_minus_offset(prevsize); + psize += prevsize; + p = prev; + if (rtcheck(ok_address(prev))) + { + // consolidate backward + if (p != _dv) + unlink_chunk(p, prevsize); + else if ((next->_head & INUSE_BITS) == INUSE_BITS) + { + _dvsize = psize; + p->set_free_with_pinuse(psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (rtcheck(ok_next(p, next) && ok_pinuse(next))) + { + if (!next->cinuse()) + { + // consolidate forward + if (next == _top) + { + size_t tsize = _topsize += psize; + _top = p; + p->_head = tsize | PINUSE_BIT; + if (p == _dv) + { + _dv = 0; + _dvsize = 0; + } + if (should_trim(tsize)) + sys_trim(0); + goto postaction; + } + else if (next == _dv) + { + size_t dsize = _dvsize += psize; + _dv = p; + p->set_size_and_pinuse_of_free_chunk(dsize); + goto postaction; + } + else + { + size_t nsize = next->chunksize(); + psize += nsize; + unlink_chunk(next, nsize); + p->set_size_and_pinuse_of_free_chunk(psize); + if (p == _dv) + { + _dvsize = psize; + goto postaction; + } + } + } + else + p->set_free_with_pinuse(psize, next); + + if (is_small(psize)) + { + insert_small_chunk(p, psize); + check_free_chunk(p); + } + else + { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(tp, psize); + check_free_chunk(p); + if (--_release_checks == 0) + release_unused_segments(); + } + goto postaction; + } + } +erroraction: + SPP_USAGE_ERROR_ACTION(this, p); +postaction: + ; + } +} + +/* ------------ Internal support for realloc, memalign, etc -------------- */ + +// Try to realloc; only in-place unless can_move true +mchunkptr malloc_state::try_realloc_chunk(mchunkptr p, size_t nb, int can_move) +{ + mchunkptr newp = 0; + size_t oldsize = p->chunksize(); + mchunkptr next = (mchunkptr)p->chunk_plus_offset(oldsize); + if (rtcheck(ok_address(p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) + { + if (p->is_mmapped()) + newp = mmap_resize(p, nb, can_move); + else if (oldsize >= nb) + { + // already big enough + size_t rsize = oldsize - nb; + if (rsize >= MIN_CHUNK_SIZE) + { + // split off remainder + mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb); + set_inuse(p, nb); + set_inuse(r, rsize); + dispose_chunk(r, rsize); + } + newp = p; + } + else if (next == _top) + { + // extend into top + if (oldsize + _topsize > nb) + { + size_t newsize = oldsize + _topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = (mchunkptr)p->chunk_plus_offset(nb); + set_inuse(p, nb); + newtop->_head = newtopsize | PINUSE_BIT; + _top = newtop; + _topsize = newtopsize; + newp = p; + } + } + else if (next == _dv) + { + // extend into dv + size_t dvs = _dvsize; + if (oldsize + dvs >= nb) + { + size_t dsize = oldsize + dvs - nb; + if (dsize >= MIN_CHUNK_SIZE) + { + mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb); + mchunkptr n = (mchunkptr)r->chunk_plus_offset(dsize); + set_inuse(p, nb); + r->set_size_and_pinuse_of_free_chunk(dsize); + n->clear_pinuse(); + _dvsize = dsize; + _dv = r; + } + else + { + // exhaust dv + size_t newsize = oldsize + dvs; + set_inuse(p, newsize); + _dvsize = 0; + _dv = 0; + } + newp = p; + } + } + else if (!next->cinuse()) + { + // extend into next free chunk + size_t nextsize = next->chunksize(); + if (oldsize + nextsize >= nb) + { + size_t rsize = oldsize + nextsize - nb; + unlink_chunk(next, nextsize); + if (rsize < MIN_CHUNK_SIZE) + { + size_t newsize = oldsize + nextsize; + set_inuse(p, newsize); + } + else + { + mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb); + set_inuse(p, nb); + set_inuse(r, rsize); + dispose_chunk(r, rsize); + } + newp = p; + } + } + } + else + SPP_USAGE_ERROR_ACTION(m, chunk2mem(p)); + return newp; +} + +void* malloc_state::internal_memalign(size_t alignment, size_t bytes) +{ + void* mem = 0; + if (alignment < MIN_CHUNK_SIZE) // must be at least a minimum chunk size + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment - 1)) != 0) + { + // Ensure a power of 2 + size_t a = SPP_MALLOC_ALIGNMENT << 1; + while (a < alignment) + a <<= 1; + alignment = a; + } + if (bytes >= MAX_REQUEST - alignment) + SPP_MALLOC_FAILURE_ACTION; + else + { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + mem = internal_malloc(req); + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); + if ((((size_t)(mem)) & (alignment - 1)) != 0) + { + // misaligned + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((void *)(((size_t)((char*)mem + alignment - 1)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE) ? + br : br + alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = p->chunksize() - leadsize; + + if (p->is_mmapped()) + { + // For mmapped chunks, just adjust offset + newp->_prev_foot = p->_prev_foot + leadsize; + newp->_head = newsize; + } + else + { + // Otherwise, give back leader, use the rest + set_inuse(newp, newsize); + set_inuse(p, leadsize); + dispose_chunk(p, leadsize); + } + p = newp; + } + + // Give back spare room at the end + if (!p->is_mmapped()) + { + size_t size = p->chunksize(); + if (size > nb + MIN_CHUNK_SIZE) + { + size_t remainder_size = size - nb; + mchunkptr remainder = (mchunkptr)p->chunk_plus_offset(nb); + set_inuse(p, nb); + set_inuse(remainder, remainder_size); + dispose_chunk(remainder, remainder_size); + } + } + + mem = chunk2mem(p); + assert(p->chunksize() >= nb); + assert(((size_t)mem & (alignment - 1)) == 0); + check_inuse_chunk(p); + } + } + return mem; +} + +/* + Common support for independent_X routines, handling + all of the combinations that can result. + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed +*/ +void** malloc_state::ialloc(size_t n_elements, size_t* sizes, int opts, + void* chunks[]) +{ + + size_t element_size; // chunksize of each element, if all same + size_t contents_size; // total size of elements + size_t array_size; // request size of pointer array + void* mem; // malloced aggregate space + mchunkptr p; // corresponding chunk + size_t remainder_size; // remaining bytes while splitting + void** marray; // either "chunks" or malloced ptr array + mchunkptr array_chunk; // chunk for malloced ptr array + flag_t was_enabled; // to disable mmap + size_t size; + size_t i; + + mparams.ensure_initialization(); + // compute array length, if needed + if (chunks != 0) + { + if (n_elements == 0) + return chunks; // nothing to do + marray = chunks; + array_size = 0; + } + else + { + // if empty req, must still return chunk representing empty array + if (n_elements == 0) + return (void**)internal_malloc(0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + // compute total element size + if (opts & 0x1) + { + // all-same-size + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else + { + // add up all the sizes + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(); + disable_mmap(); + mem = internal_malloc(size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(); + if (mem == 0) + return 0; + + p = mem2chunk(mem); + remainder_size = p->chunksize(); + + assert(!p->is_mmapped()); + + if (opts & 0x2) + { + // optionally clear the elements + memset((size_t*)mem, 0, remainder_size - sizeof(size_t) - array_size); + } + + // If not provided, allocate the pointer array as final part of chunk + if (marray == 0) + { + size_t array_chunk_size; + array_chunk = (mchunkptr)p->chunk_plus_offset(contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**)(chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + // split out elements + for (i = 0; ; ++i) + { + marray[i] = chunk2mem(p); + if (i != n_elements - 1) + { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(p, size); + p = (mchunkptr)p->chunk_plus_offset(size); + } + else + { + // the final element absorbs any overallocation slop + set_size_and_pinuse_of_inuse_chunk(p, remainder_size); + break; + } + } + +#if SPP_DEBUG + if (marray != chunks) + { + // final element must have exactly exhausted chunk + if (element_size != 0) + assert(remainder_size == element_size); + else + assert(remainder_size == request2size(sizes[i])); + check_inuse_chunk(mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(mem2chunk(marray[i])); + +#endif + + return marray; +} + +/* Try to free all pointers in the given array. + Note: this could be made faster, by delaying consolidation, + at the price of disabling some user integrity checks, We + still optimize some consolidations by combining adjacent + chunks before freeing, which will occur often if allocated + with ialloc or the array is sorted. +*/ +size_t malloc_state::internal_bulk_free(void* array[], size_t nelem) +{ + size_t unfreed = 0; + if (1) + { + void** a; + void** fence = &(array[nelem]); + for (a = array; a != fence; ++a) + { + void* mem = *a; + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); + size_t psize = p->chunksize(); +#if SPP_FOOTERS + if (get_mstate_for(p) != m) + { + ++unfreed; + continue; + } +#endif + check_inuse_chunk(p); + *a = 0; + if (rtcheck(ok_address(p) && ok_inuse(p))) + { + void ** b = a + 1; // try to merge with next chunk + mchunkptr next = (mchunkptr)p->next_chunk(); + if (b != fence && *b == chunk2mem(next)) + { + size_t newsize = next->chunksize() + psize; + set_inuse(p, newsize); + *b = chunk2mem(p); + } + else + dispose_chunk(p, psize); + } + else + { + SPP_ABORT; + break; + } + } + } + if (should_trim(_topsize)) + sys_trim(0); + } + return unfreed; +} + +void malloc_state::init(char* tbase, size_t tsize) +{ + _seg._base = _least_addr = tbase; + _seg._size = _footprint = _max_footprint = tsize; + _magic = mparams._magic; + _release_checks = SPP_MAX_RELEASE_CHECK_RATE; + _mflags = mparams._default_mflags; + _extp = 0; + _exts = 0; + disable_contiguous(); + init_bins(); + mchunkptr mn = (mchunkptr)mem2chunk(this)->next_chunk(); + init_top(mn, (size_t)((tbase + tsize) - (char*)mn) - top_foot_size()); + check_top_chunk(_top); +} + +/* Traversal */ +#if SPP_MALLOC_INSPECT_ALL +void malloc_state::internal_inspect_all(void(*handler)(void *start, void *end, + size_t used_bytes, + void* callback_arg), + void* arg) +{ + if (is_initialized()) + { + mchunkptr top = top; + msegmentptr s; + for (s = &seg; s != 0; s = s->next) + { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) + { + mchunkptr next = (mchunkptr)q->next_chunk(); + size_t sz = q->chunksize(); + size_t used; + void* start; + if (q->is_inuse()) + { + used = sz - CHUNK_OVERHEAD; // must not be mmapped + start = chunk2mem(q); + } + else + { + used = 0; + if (is_small(sz)) + { + // offset by possible bookkeeping + start = (void*)((char*)q + sizeof(struct malloc_chunk)); + } + else + start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + } + if (start < (void*)next) // skip if all space is bookkeeping + handler(start, next, used, arg); + if (q == top) + break; + q = next; + } + } + } +} +#endif // SPP_MALLOC_INSPECT_ALL + + + +/* ----------------------------- user mspaces ---------------------------- */ + +static mstate init_user_mstate(char* tbase, size_t tsize) +{ + size_t msize = pad_request(sizeof(malloc_state)); + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + msp->_head = (msize | INUSE_BITS); + m->init(tbase, tsize); + return m; +} + +SPP_API mspace create_mspace(size_t capacity, int locked) +{ + mstate m = 0; + size_t msize; + mparams.ensure_initialization(); + msize = pad_request(sizeof(malloc_state)); + if (capacity < (size_t) - (msize + top_foot_size() + mparams._page_size)) + { + size_t rs = ((capacity == 0) ? mparams._granularity : + (capacity + top_foot_size() + msize)); + size_t tsize = mparams.granularity_align(rs); + char* tbase = (char*)(SPP_CALL_MMAP(tsize)); + if (tbase != cmfail) + { + m = init_user_mstate(tbase, tsize); + m->_seg._sflags = USE_MMAP_BIT; + m->set_lock(locked); + } + } + return (mspace)m; +} + +SPP_API size_t destroy_mspace(mspace msp) +{ + size_t freed = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + { + msegmentptr sp = &ms->_seg; + while (sp != 0) + { + char* base = sp->_base; + size_t size = sp->_size; + flag_t flag = sp->_sflags; + (void)base; // placate people compiling -Wunused-variable + sp = sp->_next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + SPP_CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return freed; +} + +/* ---------------------------- mspace versions of malloc/calloc/free routines -------------------- */ +SPP_API void* mspace_malloc(mspace msp, size_t bytes) +{ + mstate ms = (mstate)msp; + if (!ms->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(ms, ms); + return 0; + } + return ms->_malloc(bytes); +} + +SPP_API void mspace_free(mspace msp, void* mem) +{ + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); +#if SPP_FOOTERS + mstate fm = get_mstate_for(p); + (void)msp; // placate people compiling -Wunused +#else + mstate fm = (mstate)msp; +#endif + if (!fm->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(fm, p); + return; + } + fm->_free(p); + } +} + +SPP_API inline void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) +{ + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ms->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(ms, ms); + return 0; + } + if (n_elements != 0) + { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = spp_max_size_t; // force downstream failure on overflow + } + mem = ms->internal_malloc(req); + if (mem != 0 && mem2chunk(mem)->calloc_must_clear()) + memset(mem, 0, req); + return mem; +} + +SPP_API inline void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) +{ + void* mem = 0; + if (oldmem == 0) + mem = mspace_malloc(msp, bytes); + else if (bytes >= MAX_REQUEST) + SPP_MALLOC_FAILURE_ACTION; +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) + mspace_free(msp, oldmem); +#endif + else + { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! SPP_FOOTERS + mstate m = (mstate)msp; +#else + mstate m = get_mstate_for(oldp); + if (!m->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif + if (1) + { + mchunkptr newp = m->try_realloc_chunk(oldp, nb, 1); + if (newp != 0) + { + m->check_inuse_chunk(newp); + mem = chunk2mem(newp); + } + else + { + mem = mspace_malloc(m, bytes); + if (mem != 0) + { + size_t oc = oldp->chunksize() - oldp->overhead_for(); + memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); + mspace_free(m, oldmem); + } + } + } + } + return mem; +} + +#if 0 + +SPP_API mspace create_mspace_with_base(void* base, size_t capacity, int locked) +{ + mstate m = 0; + size_t msize; + mparams.ensure_initialization(); + msize = pad_request(sizeof(malloc_state)); + if (capacity > msize + top_foot_size() && + capacity < (size_t) - (msize + top_foot_size() + mparams._page_size)) + { + m = init_user_mstate((char*)base, capacity); + m->_seg._sflags = EXTERN_BIT; + m->set_lock(locked); + } + return (mspace)m; +} + +SPP_API int mspace_track_large_chunks(mspace msp, int enable) +{ + int ret = 0; + mstate ms = (mstate)msp; + if (1) + { + if (!ms->use_mmap()) + ret = 1; + if (!enable) + ms->enable_mmap(); + else + ms->disable_mmap(); + } + return ret; +} + +SPP_API void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) +{ + void* mem = 0; + if (oldmem != 0) + { + if (bytes >= MAX_REQUEST) + SPP_MALLOC_FAILURE_ACTION; + else + { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! SPP_FOOTERS + mstate m = (mstate)msp; +#else + mstate m = get_mstate_for(oldp); + (void)msp; // placate people compiling -Wunused + if (!m->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif + if (1) + { + mchunkptr newp = m->try_realloc_chunk(oldp, nb, 0); + if (newp == oldp) + { + m->check_inuse_chunk(newp); + mem = oldmem; + } + } + } + } + return mem; +} + +SPP_API void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) +{ + mstate ms = (mstate)msp; + if (!ms->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(ms, ms); + return 0; + } + if (alignment <= SPP_MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); + return ms->internal_memalign(alignment, bytes); +} + +SPP_API void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) +{ + size_t sz = elem_size; // serves as 1-element array + mstate ms = (mstate)msp; + if (!ms->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(ms, ms); + return 0; + } + return ms->ialloc(n_elements, &sz, 3, chunks); +} + +SPP_API void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) +{ + mstate ms = (mstate)msp; + if (!ms->ok_magic()) + { + SPP_USAGE_ERROR_ACTION(ms, ms); + return 0; + } + return ms->ialloc(n_elements, sizes, 0, chunks); +} + +#endif + +SPP_API inline size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) +{ + return ((mstate)msp)->internal_bulk_free(array, nelem); +} + +#if SPP_MALLOC_INSPECT_ALL +SPP_API void mspace_inspect_all(mspace msp, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) +{ + mstate ms = (mstate)msp; + if (ms->ok_magic()) + internal_inspect_all(ms, handler, arg); + else + SPP_USAGE_ERROR_ACTION(ms, ms); +} +#endif + +SPP_API inline int mspace_trim(mspace msp, size_t pad) +{ + int result = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + result = ms->sys_trim(pad); + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return result; +} + +SPP_API inline size_t mspace_footprint(mspace msp) +{ + size_t result = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + result = ms->_footprint; + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return result; +} + +SPP_API inline size_t mspace_max_footprint(mspace msp) +{ + size_t result = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + result = ms->_max_footprint; + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return result; +} + +SPP_API inline size_t mspace_footprint_limit(mspace msp) +{ + size_t result = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + { + size_t maf = ms->_footprint_limit; + result = (maf == 0) ? spp_max_size_t : maf; + } + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return result; +} + +SPP_API inline size_t mspace_set_footprint_limit(mspace msp, size_t bytes) +{ + size_t result = 0; + mstate ms = (mstate)msp; + if (ms->ok_magic()) + { + if (bytes == 0) + result = mparams.granularity_align(1); // Use minimal size + if (bytes == spp_max_size_t) + result = 0; // disable + else + result = mparams.granularity_align(bytes); + ms->_footprint_limit = result; + } + else + SPP_USAGE_ERROR_ACTION(ms, ms); + return result; +} + +SPP_API inline size_t mspace_usable_size(const void* mem) +{ + if (mem != 0) + { + mchunkptr p = mem2chunk(mem); + if (p->is_inuse()) + return p->chunksize() - p->overhead_for(); + } + return 0; +} + +SPP_API inline int mspace_mallopt(int param_number, int value) +{ + return mparams.change(param_number, value); +} + +} // spp_ namespace + + +#endif // SPP_EXCLUDE_IMPLEMENTATION + +#endif // spp_dlalloc__h_ diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_memory.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_memory.h new file mode 100644 index 0000000..cfaa108 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_memory.h @@ -0,0 +1,190 @@ +#if !defined(spp_memory_h_guard) +#define spp_memory_h_guard + +#include +#include +#include + +#if defined(_WIN32) || defined( __CYGWIN__) + #define SPP_WIN +#endif + +#ifdef SPP_WIN + #include + #include + #undef min + #undef max +#elif defined(__linux__) + #include + #include +#elif defined(__FreeBSD__) + #include + #include + #include + #include + #include + #include +#endif + +namespace spp +{ + uint64_t GetSystemMemory() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPageFile); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo (&memInfo); + auto totalVirtualMem = memInfo.totalram; + + totalVirtualMem += memInfo.totalswap; + totalVirtualMem *= memInfo.mem_unit; + return static_cast(totalVirtualMem); +#elif defined(__FreeBSD__) + kvm_t *kd; + u_int pageCnt; + size_t pageCntLen = sizeof(pageCnt); + u_int pageSize; + struct kvm_swap kswap; + uint64_t totalVirtualMem; + + pageSize = static_cast(getpagesize()); + + sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); + totalVirtualMem = pageCnt * pageSize; + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + kvm_getswapinfo(kd, &kswap, 1, 0); + kvm_close(kd); + totalVirtualMem += kswap.ksw_total * pageSize; + + return totalVirtualMem; +#else + return 0; +#endif + } + + uint64_t GetTotalMemoryUsed() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo(&memInfo); + auto virtualMemUsed = memInfo.totalram - memInfo.freeram; + + virtualMemUsed += memInfo.totalswap - memInfo.freeswap; + virtualMemUsed *= memInfo.mem_unit; + + return static_cast(virtualMemUsed); +#elif defined(__FreeBSD__) + kvm_t *kd; + u_int pageSize; + u_int pageCnt, freeCnt; + size_t pageCntLen = sizeof(pageCnt); + size_t freeCntLen = sizeof(freeCnt); + struct kvm_swap kswap; + uint64_t virtualMemUsed; + + pageSize = static_cast(getpagesize()); + + sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); + sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0); + virtualMemUsed = (pageCnt - freeCnt) * pageSize; + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + kvm_getswapinfo(kd, &kswap, 1, 0); + kvm_close(kd); + virtualMemUsed += kswap.ksw_used * pageSize; + + return virtualMemUsed; +#else + return 0; +#endif + } + + uint64_t GetProcessMemoryUsed() + { +#ifdef SPP_WIN + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); + return static_cast(pmc.PrivateUsage); +#elif defined(__linux__) + auto parseLine = + [](char* line)->int + { + auto i = strlen(line); + + while(*line < '0' || *line > '9') + { + line++; + } + + line[i-3] = '\0'; + i = atoi(line); + return i; + }; + + auto file = fopen("/proc/self/status", "r"); + auto result = -1; + char line[128]; + + while(fgets(line, 128, file) != nullptr) + { + if(strncmp(line, "VmSize:", 7) == 0) + { + result = parseLine(line); + break; + } + } + + fclose(file); + return static_cast(result) * 1024; +#elif defined(__FreeBSD__) + struct kinfo_proc info; + size_t infoLen = sizeof(info); + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + + sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0); + return static_cast(info.ki_rssize * getpagesize()); +#else + return 0; +#endif + } + + uint64_t GetPhysicalMemory() + { +#ifdef SPP_WIN + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memInfo); + return static_cast(memInfo.ullTotalPhys); +#elif defined(__linux__) + struct sysinfo memInfo; + sysinfo(&memInfo); + + auto totalPhysMem = memInfo.totalram; + + totalPhysMem *= memInfo.mem_unit; + return static_cast(totalPhysMem); +#elif defined(__FreeBSD__) + u_long physMem; + size_t physMemLen = sizeof(physMem); + int mib[] = { CTL_HW, HW_PHYSMEM }; + + sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0); + return physMem; +#else + return 0; +#endif + } + +} + +#endif // spp_memory_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_smartptr.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_smartptr.h new file mode 100644 index 0000000..fba3acf --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_smartptr.h @@ -0,0 +1,71 @@ +#if !defined(spp_smartptr_h_guard) +#define spp_smartptr_h_guard + + +/* ----------------------------------------------------------------------------------------------- + * quick version of intrusive_ptr + * ----------------------------------------------------------------------------------------------- + */ + +#include +#include "spp_config.h" + +// ------------------------------------------------------------------------ +class spp_rc +{ +public: + spp_rc() : _cnt(0) {} + spp_rc(const spp_rc &) : _cnt(0) {} + void increment() const { ++_cnt; } + void decrement() const { assert(_cnt); if (--_cnt == 0) delete this; } + unsigned count() const { return _cnt; } + +protected: + virtual ~spp_rc() {} + +private: + mutable unsigned _cnt; +}; + +// ------------------------------------------------------------------------ +template +class spp_sptr +{ +public: + spp_sptr() : _p(0) {} + spp_sptr(T *p) : _p(p) { if (_p) _p->increment(); } + spp_sptr(const spp_sptr &o) : _p(o._p) { if (_p) _p->increment(); } +#ifndef SPP_NO_CXX11_RVALUE_REFERENCES + spp_sptr(spp_sptr &&o) : _p(o._p) { o._p = (T *)0; } + spp_sptr& operator=(spp_sptr &&o) { this->swap(o); return *this; } +#endif + ~spp_sptr() { if (_p) _p->decrement(); } + spp_sptr& operator=(const spp_sptr &o) { reset(o._p); return *this; } + T* get() const { return _p; } + void swap(spp_sptr &o) { T *tmp = _p; _p = o._p; o._p = tmp; } + void reset(const T *p = 0) + { + if (p == _p) + return; + if (_p) _p->decrement(); + _p = (T *)p; + if (_p) _p->increment(); + } + T* operator->() const { return const_cast(_p); } + bool operator!() const { return _p == 0; } + +private: + T *_p; +}; + +// ------------------------------------------------------------------------ +namespace std +{ + template + inline void swap(spp_sptr &a, spp_sptr &b) + { + a.swap(b); + } +} + +#endif // spp_smartptr_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_stdint.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_stdint.h new file mode 100644 index 0000000..3adced9 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_stdint.h @@ -0,0 +1,16 @@ +#if !defined(spp_stdint_h_guard) +#define spp_stdint_h_guard + +#include "spp_config.h" + +#if defined(SPP_HAS_CSTDINT) && (__cplusplus >= 201103) + #include +#else + #if defined(__FreeBSD__) || defined(__IBMCPP__) || defined(_AIX) + #include + #else + #include + #endif +#endif + +#endif // spp_stdint_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_timer.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_timer.h new file mode 100644 index 0000000..48180f4 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_timer.h @@ -0,0 +1,58 @@ +/** + Copyright (c) 2016 Mariano Gonzalez + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef spp_timer_h_guard +#define spp_timer_h_guard + +#include + +namespace spp +{ + template + class Timer + { + public: + Timer() { reset(); } + void reset() { _start = _snap = clock::now(); } + void snap() { _snap = clock::now(); } + + float get_total() const { return get_diff(_start, clock::now()); } + float get_delta() const { return get_diff(_snap, clock::now()); } + + private: + using clock = std::chrono::high_resolution_clock; + using point = std::chrono::time_point; + + template + static T get_diff(const point& start, const point& end) + { + using duration_t = std::chrono::duration; + + return std::chrono::duration_cast(end - start).count(); + } + + point _start; + point _snap; + }; +} + +#endif // spp_timer_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_traits.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_traits.h new file mode 100644 index 0000000..792f52f --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_traits.h @@ -0,0 +1,125 @@ +#if !defined(spp_traits_h_guard) +#define spp_traits_h_guard + +#include "spp_config.h" + +template class HashObject; // for Google's benchmark, not in spp namespace! + +namespace spp_ +{ + +// --------------------------------------------------------------------------- +// type_traits we need +// --------------------------------------------------------------------------- +template +struct integral_constant { static const T value = v; }; + +template const T integral_constant::value; + +typedef integral_constant true_type; +typedef integral_constant false_type; + +typedef integral_constant zero_type; +typedef integral_constant one_type; +typedef integral_constant two_type; +typedef integral_constant three_type; + +template struct is_same : public false_type { }; +template struct is_same : public true_type { }; + +template struct remove_const { typedef T type; }; +template struct remove_const { typedef T type; }; + +template struct remove_volatile { typedef T type; }; +template struct remove_volatile { typedef T type; }; + +template struct remove_cv +{ + typedef typename remove_const::type>::type type; +}; + +// ---------------- is_integral ---------------------------------------- +template struct is_integral; +template struct is_integral : false_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +#ifdef SPP_HAS_LONG_LONG + template<> struct is_integral : true_type { }; + template<> struct is_integral : true_type { }; +#endif +template struct is_integral : is_integral { }; +template struct is_integral : is_integral { }; +template struct is_integral : is_integral { }; + +// ---------------- is_floating_point ---------------------------------------- +template struct is_floating_point; +template struct is_floating_point : false_type { }; +template<> struct is_floating_point : true_type { }; +template<> struct is_floating_point : true_type { }; +template<> struct is_floating_point : true_type { }; +template struct is_floating_point : is_floating_point { }; +template struct is_floating_point : is_floating_point { }; +template struct is_floating_point : is_floating_point { }; + +// ---------------- is_pointer ---------------------------------------- +template struct is_pointer; +template struct is_pointer : false_type { }; +template struct is_pointer : true_type { }; +template struct is_pointer : is_pointer { }; +template struct is_pointer : is_pointer { }; +template struct is_pointer : is_pointer { }; + +// ---------------- is_reference ---------------------------------------- +template struct is_reference; +template struct is_reference : false_type {}; +template struct is_reference : true_type {}; + +// ---------------- is_relocatable ---------------------------------------- +// relocatable values can be moved around in memory using memcpy and remain +// correct. Most types are relocatable, an example of a type who is not would +// be a struct which contains a pointer to a buffer inside itself - this is the +// case for std::string in gcc 5. +// ------------------------------------------------------------------------ +template struct is_relocatable; +template struct is_relocatable : + integral_constant::value || + is_floating_point::value || + is_pointer::value + )> +{ }; + +template struct is_relocatable > : true_type { }; + +template struct is_relocatable : is_relocatable { }; +template struct is_relocatable : is_relocatable { }; +template struct is_relocatable : is_relocatable { }; +template struct is_relocatable : is_relocatable { }; +template struct is_relocatable > : + integral_constant::value && is_relocatable::value)> +{ }; + +// A template helper used to select A or B based on a condition. +// ------------------------------------------------------------ +template +struct if_ +{ + typedef A type; +}; + +template +struct if_ +{ + typedef B type; +}; + +} // spp_ namespace + +#endif // spp_traits_h_guard diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_utils.h b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_utils.h new file mode 100644 index 0000000..4f2e925 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/external/sparsepp/sparsepp-e40d7a0/sparsepp/spp_utils.h @@ -0,0 +1,477 @@ +// ---------------------------------------------------------------------- +// Copyright (c) 2016, Steven Gregory Popovitch - greg7mdp@gmail.com +// All rights reserved. +// +// Code derived derived from Boost libraries. +// Boost software licence reproduced below. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * The name of Steven Gregory Popovitch may not be used to +// endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// ---------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// --------------------------------------------------------------------------- + +// ---------------------------------------------------------------------- +// H A S H F U N C T I O N S +// ---------------------------- +// +// Implements spp::spp_hash() and spp::hash_combine() +// ---------------------------------------------------------------------- + +#if !defined(spp_utils_h_guard_) +#define spp_utils_h_guard_ + +#if defined(_MSC_VER) + #if (_MSC_VER >= 1600 ) // vs2010 (1900 is vs2015) + #include + #define SPP_HASH_CLASS std::hash + #else + #include + #define SPP_HASH_CLASS stdext::hash_compare + #endif + #if (_MSC_FULL_VER < 190021730) + #define SPP_NO_CXX11_NOEXCEPT + #endif +#elif defined __clang__ + #if __has_feature(cxx_noexcept) || defined(SPP_CXX11) // define SPP_CXX11 if your compiler has + #include + #define SPP_HASH_CLASS std::hash + #else + #include + #define SPP_HASH_CLASS std::tr1::hash + #endif + + #if !__has_feature(cxx_noexcept) + #define SPP_NO_CXX11_NOEXCEPT + #endif +#elif defined(__GNUC__) + #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) + #include + #define SPP_HASH_CLASS std::hash + + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40600 + #define SPP_NO_CXX11_NOEXCEPT + #endif + #else + #include + #define SPP_HASH_CLASS std::tr1::hash + #define SPP_NO_CXX11_NOEXCEPT + #endif +#else + #include + #define SPP_HASH_CLASS std::hash +#endif + +#ifdef SPP_NO_CXX11_NOEXCEPT + #define SPP_NOEXCEPT +#else + #define SPP_NOEXCEPT noexcept +#endif + +#ifdef SPP_NO_CXX11_CONSTEXPR + #define SPP_CONSTEXPR +#else + #define SPP_CONSTEXPR constexpr +#endif + +#ifdef SPP_NO_CXX14_CONSTEXPR + #define SPP_CXX14_CONSTEXPR +#else + #define SPP_CXX14_CONSTEXPR constexpr +#endif + +#define SPP_INLINE + +#ifndef spp_ + #define spp_ spp +#endif + +namespace spp_ +{ + +template T spp_min(T a, T b) { return a < b ? a : b; } +template T spp_max(T a, T b) { return a >= b ? a : b; } + +template +struct spp_hash +{ + SPP_INLINE size_t operator()(const T &__v) const SPP_NOEXCEPT + { + SPP_HASH_CLASS hasher; + return hasher(__v); + } +}; + +template +struct spp_hash +{ + static size_t spp_log2 (size_t val) SPP_NOEXCEPT + { + size_t res = 0; + while (val > 1) + { + val >>= 1; + res++; + } + return res; + } + + SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT + { + static const size_t shift = 3; // spp_log2(1 + sizeof(T)); // T might be incomplete! + const uintptr_t i = (const uintptr_t)__v; + return static_cast(i >> shift); + } +}; + +// from http://burtleburtle.net/bob/hash/integer.html +// fast and efficient for power of two table sizes where we always +// consider the last bits. +// --------------------------------------------------------------- +inline size_t spp_mix_32(uint32_t a) +{ + a = a ^ (a >> 4); + a = (a ^ 0xdeadbeef) + (a << 5); + a = a ^ (a >> 11); + return static_cast(a); +} + +// More thorough scrambling as described in +// https://gist.github.com/badboy/6267743 +// ---------------------------------------- +inline size_t spp_mix_64(uint64_t a) +{ + a = (~a) + (a << 21); // a = (a << 21) - a - 1; + a = a ^ (a >> 24); + a = (a + (a << 3)) + (a << 8); // a * 265 + a = a ^ (a >> 14); + a = (a + (a << 2)) + (a << 4); // a * 21 + a = a ^ (a >> 28); + a = a + (a << 31); + return static_cast(a); +} + +template +struct spp_unary_function +{ + typedef ArgumentType argument_type; + typedef ResultType result_type; +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT + { return static_cast(__v); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT + { return static_cast(__v); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT + { return static_cast(__v); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT + { return static_cast(__v); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT + { return static_cast(__v); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT + { return spp_mix_32(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT + { return spp_mix_64(static_cast(__v)); } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(float __v) const SPP_NOEXCEPT + { + // -0.0 and 0.0 should return same hash + uint32_t *as_int = reinterpret_cast(&__v); + return (__v == 0) ? static_cast(0) : spp_mix_32(*as_int); + } +}; + +template <> +struct spp_hash : public spp_unary_function +{ + SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT + { + // -0.0 and 0.0 should return same hash + uint64_t *as_int = reinterpret_cast(&__v); + return (__v == 0) ? static_cast(0) : spp_mix_64(*as_int); + } +}; + +template struct Combiner +{ + inline void operator()(T& seed, T value); +}; + +template struct Combiner +{ + inline void operator()(T& seed, T value) + { + seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } +}; + +template struct Combiner +{ + inline void operator()(T& seed, T value) + { + seed ^= value + T(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2); + } +}; + +template +inline void hash_combine(std::size_t& seed, T const& v) +{ + spp_::spp_hash hasher; + Combiner combiner; + + combiner(seed, hasher(v)); +} + +static inline uint32_t s_spp_popcount_default(uint32_t i) SPP_NOEXCEPT +{ + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; +} + +static inline uint32_t s_spp_popcount_default(uint64_t x) SPP_NOEXCEPT +{ + const uint64_t m1 = uint64_t(0x5555555555555555); // binary: 0101... + const uint64_t m2 = uint64_t(0x3333333333333333); // binary: 00110011.. + const uint64_t m4 = uint64_t(0x0f0f0f0f0f0f0f0f); // binary: 4 zeros, 4 ones ... + const uint64_t h01 = uint64_t(0x0101010101010101); // the sum of 256 to the power of 0,1,2,3... + + x -= (x >> 1) & m1; // put count of each 2 bits into those 2 bits + x = (x & m2) + ((x >> 2) & m2); // put count of each 4 bits into those 4 bits + x = (x + (x >> 4)) & m4; // put count of each 8 bits into those 8 bits + return (x * h01)>>56; // returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24)+... +} + +#ifdef __APPLE__ + static inline uint32_t count_trailing_zeroes(size_t v) SPP_NOEXCEPT + { + size_t x = (v & -v) - 1; + // sadly sizeof() required to build on macos + return sizeof(size_t) == 8 ? s_spp_popcount_default((uint64_t)x) : s_spp_popcount_default((uint32_t)x); + } + + static inline uint32_t s_popcount(size_t v) SPP_NOEXCEPT + { + // sadly sizeof() required to build on macos + return sizeof(size_t) == 8 ? s_spp_popcount_default((uint64_t)v) : s_spp_popcount_default((uint32_t)v); + } +#else + static inline uint32_t count_trailing_zeroes(size_t v) SPP_NOEXCEPT + { + return s_spp_popcount_default((v & -(intptr_t)v) - 1); + } + + static inline uint32_t s_popcount(size_t v) SPP_NOEXCEPT + { + return s_spp_popcount_default(v); + } +#endif + +// ----------------------------------------------------------- +// ----------------------------------------------------------- +template +class libc_allocator +{ +public: + typedef T value_type; + typedef T* pointer; + typedef ptrdiff_t difference_type; + typedef const T* const_pointer; + typedef size_t size_type; + + libc_allocator() {} + libc_allocator(const libc_allocator&) {} + + template + libc_allocator(const libc_allocator &) {} + + libc_allocator& operator=(const libc_allocator &) { return *this; } + + template + libc_allocator& operator=(const libc_allocator &) { return *this; } + +#ifndef SPP_NO_CXX11_RVALUE_REFERENCES + libc_allocator(libc_allocator &&) {} + libc_allocator& operator=(libc_allocator &&) { return *this; } +#endif + + pointer allocate(size_t n, const_pointer /* unused */= 0) + { + pointer res = static_cast(malloc(n * sizeof(T))); + if (!res) + throw std::bad_alloc(); + return res; + } + + void deallocate(pointer p, size_t /* unused */) + { + free(p); + } + + pointer reallocate(pointer p, size_t new_size) + { + pointer res = static_cast(realloc(p, new_size * sizeof(T))); + if (!res) + throw std::bad_alloc(); + return res; + } + + // extra API to match spp_allocator interface + pointer reallocate(pointer p, size_t /* old_size */, size_t new_size) + { + return static_cast(realloc(p, new_size * sizeof(T))); + } + + size_type max_size() const + { + return static_cast(-1) / sizeof(value_type); + } + + void construct(pointer p, const value_type& val) + { + new(p) value_type(val); + } + + void destroy(pointer p) { p->~value_type(); } + + template + struct rebind + { + typedef spp_::libc_allocator other; + }; + +}; + +// forward declaration +// ------------------- +template +class spp_allocator; + +} + +template +inline bool operator==(const spp_::libc_allocator &, const spp_::libc_allocator &) +{ + return true; +} + +template +inline bool operator!=(const spp_::libc_allocator &, const spp_::libc_allocator &) +{ + return false; +} + +#endif // spp_utils_h_guard_ + diff --git a/thirdparty/graph-tools-master/include/graphalign/DagAlignerAffine.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/DagAlignerAffine.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/DagAlignerAffine.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/DagAlignerAffine.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/GappedAligner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GappedAligner.hh old mode 100755 new mode 100644 similarity index 58% rename from thirdparty/graph-tools-master/include/graphalign/GappedAligner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GappedAligner.hh index db9d3e5..504011e --- a/thirdparty/graph-tools-master/include/graphalign/GappedAligner.hh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GappedAligner.hh @@ -41,34 +41,74 @@ namespace graphtools { +enum class AlignerType +{ + PATH_ALIGNER, + DAG_ALIGNER +}; + +/// Implements alignment details that are independent of the graph +class AlignerSelector +{ + std::shared_ptr ptrPathAligner_; + mutable std::shared_ptr ptrDagAligner_; + +public: + explicit AlignerSelector( + const AlignerType alignerType, const LinearAlignmentParameters& alignerParameters = LinearAlignmentParameters()) + { + if (alignerType == AlignerType::PATH_ALIGNER) + { + ptrPathAligner_ = std::make_shared( + alignerParameters.matchScore, alignerParameters.mismatchScore, alignerParameters.gapOpenScore); + } + else if (alignerType == AlignerType::DAG_ALIGNER) + { + ptrDagAligner_ = std::make_shared( + alignerParameters.matchScore, alignerParameters.mismatchScore, alignerParameters.gapOpenScore, + alignerParameters.gapExtendScore); + } + else + { + throw std::invalid_argument( + "AlignerType " + std::to_string(static_cast(alignerType)) + " is not available"); + } + } + + std::list + suffixAlign(const Path& seed_path, const std::string& query_piece, size_t extension_len, int& score) const + { + return ptrPathAligner_ ? ptrPathAligner_->suffixAlign(seed_path, query_piece, extension_len, score) + : ptrDagAligner_->suffixAlign(seed_path, query_piece, extension_len, score); + } + + std::list + prefixAlign(const Path& seed_path, const std::string& query_piece, size_t extension_len, int& score) const + { + return ptrPathAligner_ ? ptrPathAligner_->prefixAlign(seed_path, query_piece, extension_len, score) + : ptrDagAligner_->prefixAlign(seed_path, query_piece, extension_len, score); + } +}; + using PathAndAlignment = std::pair; /** * General graph aligner supporting linear gaps. */ -class GappedGraphAligner : public GraphAligner +class GappedGraphAligner { public: /** - * Initializes the aligner - * * @param graph: A graph possibly containing loops (but no cycles) * @param kmer_len: Kmer length for kmer index * @param padding_len: Elongate paths by this much during path kmer extension step to allow for gaps * @param seed_affix_trim_len: Trim length for the prefix and suffix (=affix) of the path - * @param match_score: Score for matching bases - * @param mismatch_score: Score for mismatching bases - * @param gap_open_score: Score for opeaning a gap (linear) - * @param gap_extend_score: Score for extending an open gap (linear) */ - GappedGraphAligner( - const Graph* graph_ptr, size_t kmer_len, size_t padding_len, size_t seed_affix_trim_len, - const std::string& alignerName, LinearAlignmentParameters alignerParameters = LinearAlignmentParameters()) + GappedGraphAligner(const Graph* graph_ptr, size_t kmer_len, size_t padding_len, size_t seed_affix_trim_len) : kmer_len_(kmer_len) , padding_len_(padding_len) , seed_affix_trim_len_(seed_affix_trim_len) , kmer_index_(*graph_ptr, kmer_len) - , aligner_(alignerName, alignerParameters) { } @@ -78,7 +118,7 @@ public: * @param query: Query sequence * @return List of top-scoring graph alignments */ - std::list align(const std::string& query) const override; + std::list align(const std::string& query, AlignerSelector& alignerSelector) const; /** * Extends a seed path corresponding to a perfect match to the query sequence to full-length alignments @@ -88,8 +128,8 @@ public: * @param seed_start_on_query: Position of the left-most base of the seed on the query sequence * @return List of top-scoring graph alignments going through the seed path */ - std::list - extendSeedToFullAlignments(Path seed_path, const std::string& query, size_t seed_start_on_query) const; + std::list extendSeedToFullAlignments( + Path seed_path, const std::string& query, size_t seed_start_on_query, AlignerSelector& alignerSelector) const; /** * Aligns query suffix to all suffix-extensions of a given path @@ -99,8 +139,9 @@ public: * @param extension_len: Length of suffix-extensions * @return List of top-scoring alignments and their paths; each path is extended to contain the seed path */ - std::list - extendAlignmentPrefix(const Path& seed_path, const std::string& query_piece, size_t extension_len) const; + std::list extendAlignmentPrefix( + const Path& seed_path, const std::string& query_piece, size_t extension_len, + AlignerSelector& alignerSelector) const; /** * Aligns query prefix to all prefix-extensions of a given path @@ -110,8 +151,9 @@ public: * @param extension_len: Length of prefix-extensions * @return List of top-scoring alignments and their paths; each path is extended to contain the seed path */ - std::list - extendAlignmentSuffix(const Path& seed_path, const std::string& query_piece, size_t extension_len) const; + std::list extendAlignmentSuffix( + const Path& seed_path, const std::string& query_piece, size_t extension_len, + AlignerSelector& alignerSelector) const; private: const size_t kmer_len_; @@ -133,46 +175,5 @@ private: // Performs a search for an alignment seed boost::optional searchForAlignmentSeed(const std::string& query) const; - - class AlignerSelector - { - std::shared_ptr ptrPathAligner_; - mutable std::shared_ptr ptrDagAligner_; - - public: - AlignerSelector(const std::string& alignerName, const LinearAlignmentParameters& alignerParameters) - { - if ("path-aligner" == alignerName) - { - ptrPathAligner_ = std::make_shared( - alignerParameters.matchScore, alignerParameters.mismatchScore, alignerParameters.gapOpenScore); - } - else if ("dag-aligner" == alignerName) - { - ptrDagAligner_ = std::make_shared( - alignerParameters.matchScore, alignerParameters.mismatchScore, alignerParameters.gapOpenScore, - alignerParameters.gapExtendScore); - } - else - { - throw std::invalid_argument("Aligner " + alignerName + " is not available"); - } - } - - std::list - suffixAlign(const Path& seed_path, const std::string& query_piece, size_t extension_len, int& score) const - { - return ptrPathAligner_ ? ptrPathAligner_->suffixAlign(seed_path, query_piece, extension_len, score) - : ptrDagAligner_->suffixAlign(seed_path, query_piece, extension_len, score); - } - - std::list - prefixAlign(const Path& seed_path, const std::string& query_piece, size_t extension_len, int& score) const - { - return ptrPathAligner_ ? ptrPathAligner_->prefixAlign(seed_path, query_piece, extension_len, score) - : ptrDagAligner_->prefixAlign(seed_path, query_piece, extension_len, score); - } - - } aligner_; }; } diff --git a/thirdparty/graph-tools-master/include/graphalign/GraphAligner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAligner.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/GraphAligner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAligner.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/GraphAlignment.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAlignment.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/GraphAlignment.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAlignment.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/GraphAlignmentOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAlignmentOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/GraphAlignmentOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/GraphAlignmentOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/KmerIndex.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/KmerIndex.hh old mode 100755 new mode 100644 similarity index 88% rename from thirdparty/graph-tools-master/include/graphalign/KmerIndex.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/KmerIndex.hh index 5a8c8a2..9809b9b --- a/thirdparty/graph-tools-master/include/graphalign/KmerIndex.hh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/KmerIndex.hh @@ -21,10 +21,8 @@ #pragma once -#include #include #include -#include #include #include "graphcore/Graph.hh" @@ -33,14 +31,11 @@ namespace graphtools { -typedef std::unordered_map> StringToPathsMap; - // Kmer index holds paths that correspond to each kmer that appears in the graph and supports a few standard operations. class KmerIndex { public: explicit KmerIndex(const Graph& graph, int32_t kmer_len = 12); - explicit KmerIndex(const StringToPathsMap& kmer_to_paths_map); KmerIndex(const KmerIndex& other); KmerIndex(KmerIndex&& other) noexcept; KmerIndex& operator=(const KmerIndex& other); @@ -48,7 +43,11 @@ public: ~KmerIndex(); bool operator==(const KmerIndex& other) const; std::string encode() const; - const std::list& getPaths(const std::string& kmer) const; + + /// \brief Return all paths for kmer + /// + /// Calling this with kmer that isn't contained in the index triggers a runtime error + std::vector getPaths(const std::string& kmer) const; bool contains(const std::string& kmer) const; size_t numPaths(const std::string& kmer) const; std::unordered_set kmers() const; diff --git a/thirdparty/graph-tools-master/include/graphalign/KmerIndexOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/KmerIndexOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/KmerIndexOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/KmerIndexOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/LinearAlignment.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignment.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/LinearAlignment.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignment.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/LinearAlignmentOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignmentOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/LinearAlignmentOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignmentOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/LinearAlignmentParameters.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignmentParameters.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/LinearAlignmentParameters.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/LinearAlignmentParameters.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/Operation.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/Operation.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/Operation.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/Operation.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/OperationOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/OperationOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/OperationOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/OperationOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/PinnedAligner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedAligner.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/PinnedAligner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedAligner.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/PinnedDagAligner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedDagAligner.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/PinnedDagAligner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedDagAligner.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/PinnedPathAligner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedPathAligner.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/PinnedPathAligner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/PinnedPathAligner.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/TracebackMatrix.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/TracebackMatrix.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/TracebackMatrix.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/TracebackMatrix.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/TracebackRunner.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/TracebackRunner.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/TracebackRunner.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/TracebackRunner.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/dagAligner/AffineAlignMatrix.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/AffineAlignMatrix.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/dagAligner/AffineAlignMatrix.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/AffineAlignMatrix.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/dagAligner/AffineAlignMatrixVectorized.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/AffineAlignMatrixVectorized.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/dagAligner/AffineAlignMatrixVectorized.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/AffineAlignMatrixVectorized.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/dagAligner/BaseMatchingPenaltyMatrix.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/BaseMatchingPenaltyMatrix.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/dagAligner/BaseMatchingPenaltyMatrix.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/BaseMatchingPenaltyMatrix.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/dagAligner/Details.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/Details.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/dagAligner/Details.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/Details.hh diff --git a/thirdparty/graph-tools-master/include/graphalign/dagAligner/PenaltyMatrix.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/PenaltyMatrix.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphalign/dagAligner/PenaltyMatrix.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphalign/dagAligner/PenaltyMatrix.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/Graph.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Graph.hh old mode 100755 new mode 100644 similarity index 92% rename from thirdparty/graph-tools-master/include/graphcore/Graph.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Graph.hh index 24fad95..03ad232 --- a/thirdparty/graph-tools-master/include/graphcore/Graph.hh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Graph.hh @@ -55,18 +55,16 @@ struct Node class Graph { public: - explicit Graph(size_t num_nodes = 0, std::string const& id = "", bool is_sequence_expansion_required = true) + explicit Graph(size_t num_nodes = 0, std::string const& id = "") : graphId(id) { init(num_nodes); - is_sequence_expansion_required_ = is_sequence_expansion_required; } virtual ~Graph() = default; size_t numNodes() const { return nodes_.size(); } size_t numEdges() const { return edge_labels_.size(); } - bool isSequenceExpansionRequired() const { return is_sequence_expansion_required_; } const std::string& nodeName(NodeId node_id) const; void setNodeName(NodeId node_id, const std::string& node_name); const std::string& nodeSeq(NodeId node_id) const; @@ -97,7 +95,6 @@ private: void assertNodeExists(NodeId node_id) const; void assertEdgeExists(NodeIdPair edge) const; std::vector nodes_; - bool is_sequence_expansion_required_; std::unordered_map edge_labels_; AdjacencyList adjacency_list_; AdjacencyList reverse_adjacency_list_; @@ -115,7 +112,6 @@ public: size_t numNodes() const { return graph_.numNodes(); } size_t numEdges() const { return graph_.numEdges(); } - bool isSequenceExpansionRequired() const { return graph_.isSequenceExpansionRequired(); } const std::string& nodeName(NodeId nodeId) const { return graph_.nodeName(nodeId); } const std::string nodeSeq(NodeId nodeId) const { @@ -137,5 +133,4 @@ public: const std::set& successors(NodeId nodeId) const { return graph_.predecessors(nodeId); } const std::set& predecessors(NodeId nodeId) const { return graph_.successors(nodeId); } }; - } diff --git a/thirdparty/graph-tools-master/include/graphcore/GraphBuilders.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphBuilders.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/GraphBuilders.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphBuilders.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/GraphCoordinates.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphCoordinates.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/GraphCoordinates.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphCoordinates.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/GraphOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/GraphOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/GraphReferenceMapping.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphReferenceMapping.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/GraphReferenceMapping.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/GraphReferenceMapping.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/Path.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Path.hh old mode 100755 new mode 100644 similarity index 81% rename from thirdparty/graph-tools-master/include/graphcore/Path.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Path.hh index a653fa1..ddf3528 --- a/thirdparty/graph-tools-master/include/graphcore/Path.hh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/Path.hh @@ -21,9 +21,7 @@ #pragma once #include -#include -#include -#include +#include #include #include @@ -40,32 +38,32 @@ class Path { public: typedef std::vector::const_iterator const_iterator; - // The constructor checks if the inputs define a well-formed path. + + /// Checks if the inputs define a well-formed path. Path(const Graph* graph_raw_ptr, int32_t start_position, const std::vector& nodes, int32_t end_position); - ~Path(); - Path(const Path& other); - Path(Path&& other) noexcept; - Path& operator=(const Path& other); - Path& operator=(Path&& other) noexcept; + ~Path() = default; + bool operator==(const Path& other) const; bool operator<(const Path& path) const; - const_iterator begin() const; - const_iterator end() const; + const_iterator begin() const { return nodes_.begin(); } + const_iterator end() const { return nodes_.end(); } - // Ids of nodes overlapped by the path - std::vector const& nodeIds() const; - size_t numNodes() const; - // Sequence of the entire path + /// Ids of nodes overlapped by the path + const std::vector& nodeIds() const { return nodes_; } + size_t numNodes() const { return nodeIds().size(); } + + /// Sequence of the entire path std::string seq() const; - // Piece of node sequence that the path overlaps + + /// Piece of node sequence that the path overlaps std::string getNodeSeq(size_t node_index) const; - const Graph* graphRawPtr() const; + const Graph* graphRawPtr() const { return graph_raw_ptr_; } std::string encode() const; - int32_t startPosition() const; - int32_t endPosition() const; + int32_t startPosition() const { return start_position_; } + int32_t endPosition() const { return end_position_; } size_t length() const; - NodeId getNodeIdByIndex(size_t node_index) const; + NodeId getNodeIdByIndex(const size_t node_index) const { return nodes_[node_index]; } int32_t getStartPositionOnNodeByIndex(size_t node_index) const; int32_t getEndPositionOnNodeByIndex(size_t node_index) const; size_t getNodeOverlapLengthByIndex(size_t node_index) const; @@ -110,8 +108,19 @@ public: NodeId lastNodeId() const { return nodeIds().back(); } private: - struct Impl; - std::unique_ptr pimpl_; + void assertValidity() const; + bool isNodePositionValid(NodeId node_id, int32_t position) const; + void assertPositionsOrdered() const; + void assertNonEmpty() const; + void assertFirstNodePosValid() const; + void assertLastNodePosValid() const; + void assertConnected() const; + void assertThatIndexIsValid(int32_t node_index) const; + + const Graph* graph_raw_ptr_; + int32_t start_position_; + int32_t end_position_; + std::vector nodes_; }; std::ostream& operator<<(std::ostream& os, const Path& path); @@ -151,10 +160,7 @@ public: void shiftEndAlongNode(int32_t shift_len) { path_.shiftStartAlongNode(shift_len); } void extendEndToNode(NodeId node_id) { path_.extendStartToNode(node_id); } - friend std::ostream& operator<<(std::ostream& os, const ReversePath& path) - { - return os << "reverse path of: " << path.path_; - } + friend std::ostream& operator<<(std::ostream& os, const ReversePath& path); }; class ConstReversePath @@ -184,5 +190,4 @@ public: const Graph* graphRawPtr() const { return path_.graphRawPtr(); } }; - } diff --git a/thirdparty/graph-tools-master/include/graphcore/PathFamily.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathFamily.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/PathFamily.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathFamily.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/PathFamilyOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathFamilyOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/PathFamilyOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathFamilyOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphcore/PathOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphcore/PathOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphcore/PathOperations.hh diff --git a/thirdparty/graph-tools-master/include/graphio/AlignmentWriter.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphio/AlignmentWriter.hh old mode 100755 new mode 100644 similarity index 99% rename from thirdparty/graph-tools-master/include/graphio/AlignmentWriter.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphio/AlignmentWriter.hh index 58e1794..e075b9a --- a/thirdparty/graph-tools-master/include/graphio/AlignmentWriter.hh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphio/AlignmentWriter.hh @@ -49,5 +49,4 @@ public: const std::string& locusId, const std::string& fragmentName, const std::string& query, bool isFirstMate, bool isReversed, bool isMateReversed, const GraphAlignment& alignment) override; }; - } diff --git a/thirdparty/graph-tools-master/include/graphio/GraphJson.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphio/GraphJson.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphio/GraphJson.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphio/GraphJson.hh diff --git a/thirdparty/graph-tools-master/include/graphutils/BaseMatching.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/BaseMatching.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/BaseMatching.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/BaseMatching.hh diff --git a/thirdparty/graph-tools-master/include/graphutils/DepthTest.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/DepthTest.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/DepthTest.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/DepthTest.hh diff --git a/thirdparty/graph-tools-master/include/graphutils/IntervalBuffer.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/IntervalBuffer.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/IntervalBuffer.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/IntervalBuffer.hh diff --git a/thirdparty/graph-tools-master/include/graphutils/IntervalList.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/IntervalList.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/IntervalList.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/IntervalList.hh diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/KmerEncoding.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/KmerEncoding.hh new file mode 100644 index 0000000..cecb366 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/KmerEncoding.hh @@ -0,0 +1,103 @@ +// +// GraphTools library +// Copyright 2017-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#include +#include +#include + +namespace graphtools +{ + +struct TwoBitKmerEncoder +{ + using KmerKey_t = uint32_t; + + explicit TwoBitKmerEncoder(const size_t kmerLength) + : kmerLength_(kmerLength) + { + const size_t maxKeyBitCount(8 * sizeof(KmerKey_t)); + if (maxKeyBitCount < (kmerLength_ * 2)) + { + throw std::logic_error( + "Can't support kmer size of " + std::to_string(kmerLength_) + " with a " + + std::to_string(maxKeyBitCount) + "bit key type."); + } + } + + KmerKey_t encode(const std::string& kmer) const + { + if (kmer.size() != kmerLength_) + { + throw std::logic_error( + "kmer size (" + std::to_string(kmer.size()) + ") does not match expected size (" + + std::to_string(kmerLength_) + "), for kmer '" + kmer + "'."); + } + + KmerKey_t kmerKey(0); + for (unsigned i(0); i < kmerLength_; ++i) + { + kmerKey = (kmerKey << 2) | baseToIndex(kmer[i]); + } + return kmerKey; + } + + std::string decode(KmerKey_t kmerKey) const + { + std::string kmer(kmerLength_, 'N'); + for (unsigned i(0); i < kmerLength_; ++i) + { + kmer[kmerLength_ - (i + 1)] = indexToBase(kmerKey & 0x3); + kmerKey >>= 2; + } + return kmer; + } + +private: + static uint8_t baseToIndex(const char c) { return baseToIndex_.table[static_cast(c)]; } + + static char indexToBase(const uint8_t i) + { + static const char bases[] = "ACGT"; + if (i > 3) + { + throw std::logic_error("Unexpected kmer index: '" + std::to_string(i) + "'"); + } + return bases[i]; + } + + struct BaseToIndex + { + BaseToIndex() + { + std::fill(table, table + 256, 0); + table[static_cast('C')] = 1; + table[static_cast('G')] = 2; + table[static_cast('T')] = 3; + } + + uint8_t table[256]; + }; + + static BaseToIndex baseToIndex_; + size_t kmerLength_; +}; +} diff --git a/thirdparty/graph-tools-master/include/graphutils/PairHashing.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/PairHashing.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/PairHashing.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/PairHashing.hh diff --git a/thirdparty/graph-tools-master/include/graphutils/SequenceOperations.hh b/ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/SequenceOperations.hh old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/include/graphutils/SequenceOperations.hh rename to ehunter/thirdparty/graph-tools-master-f421f4c/include/graphutils/SequenceOperations.hh diff --git a/thirdparty/graph-tools-master/src/graphalign/GappedAligner.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GappedAligner.cpp old mode 100755 new mode 100644 similarity index 89% rename from thirdparty/graph-tools-master/src/graphalign/GappedAligner.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GappedAligner.cpp index b153640..268fd3a --- a/thirdparty/graph-tools-master/src/graphalign/GappedAligner.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GappedAligner.cpp @@ -97,7 +97,7 @@ static int32_t trimSuffixNearNodeEdge(int32_t requested_trim_len, int32_t min_pa return actual_trim_len; } -list GappedGraphAligner::align(const string& query) const +list GappedGraphAligner::align(const string& query, AlignerSelector& alignerSelector) const { try { @@ -111,7 +111,8 @@ list GappedGraphAligner::align(const string& query) const const int kMinPathLength = 2; trimSuffixNearNodeEdge(seed_affix_trim_len_, kMinPathLength, seed_path); const int trimmed_prefix_len = trimPrefixNearNodeEdge(seed_affix_trim_len_, kMinPathLength, seed_path); - return extendSeedToFullAlignments(seed_path, query, seed_start_on_query + trimmed_prefix_len); + return extendSeedToFullAlignments( + seed_path, query, seed_start_on_query + trimmed_prefix_len, alignerSelector); } else { @@ -146,7 +147,7 @@ optional GappedGraphAligner::searchForAlignme if (num_kmer_paths == 1) { - const Path& kmer_path = kmer_index_.getPaths(kmer).front(); + const Path kmer_path = kmer_index_.getPaths(kmer).front(); // This call updates kmer_start_position to the start of the extended path Path extended_path = extendPathMatching(kmer_path, upperQuery, kmer_start_position); @@ -208,8 +209,8 @@ optional GappedGraphAligner::searchForAlignme return optional_seed; } -list -GappedGraphAligner::extendSeedToFullAlignments(Path seed_path, const string& query, size_t seed_start_on_query) const +list GappedGraphAligner::extendSeedToFullAlignments( + Path seed_path, const string& query, size_t seed_start_on_query, AlignerSelector& alignerSelector) const { assert(seed_path.length() > 1); @@ -221,7 +222,8 @@ GappedGraphAligner::extendSeedToFullAlignments(Path seed_path, const string& que const string query_prefix = query.substr(0, query_prefix_len); Path prefix_seed_path = seed_path; prefix_seed_path.shrinkEndBy(seed_path.length()); - prefix_extensions = extendAlignmentPrefix(prefix_seed_path, query_prefix, query_prefix_len + padding_len_); + prefix_extensions + = extendAlignmentPrefix(prefix_seed_path, query_prefix, query_prefix_len + padding_len_, alignerSelector); } else { @@ -242,7 +244,8 @@ GappedGraphAligner::extendSeedToFullAlignments(Path seed_path, const string& que const string query_suffix = query.substr(query_prefix_len + seed_path.length(), query_suffix_len); Path suffix_seed_path = seed_path; suffix_seed_path.shrinkStartBy(seed_path.length()); - suffix_extensions = extendAlignmentSuffix(suffix_seed_path, query_suffix, query_suffix_len + padding_len_); + suffix_extensions + = extendAlignmentSuffix(suffix_seed_path, query_suffix, query_suffix_len + padding_len_, alignerSelector); } else { @@ -290,14 +293,14 @@ GappedGraphAligner::extendSeedToFullAlignments(Path seed_path, const string& que return top_graph_alignments; } -list -GappedGraphAligner::extendAlignmentPrefix(const Path& seed_path, const string& query_piece, size_t extension_len) const +list GappedGraphAligner::extendAlignmentPrefix( + const Path& seed_path, const string& query_piece, size_t extension_len, AlignerSelector& alignerSelector) const { assert(seed_path.length() == 0); int32_t top_alignment_score = INT32_MIN; list top_paths_and_alignments - = aligner_.suffixAlign(seed_path, query_piece, extension_len, top_alignment_score); + = alignerSelector.suffixAlign(seed_path, query_piece, extension_len, top_alignment_score); for (PathAndAlignment& path_and_alignment : top_paths_and_alignments) { @@ -317,14 +320,14 @@ GappedGraphAligner::extendAlignmentPrefix(const Path& seed_path, const string& q return top_paths_and_alignments; } -list -GappedGraphAligner::extendAlignmentSuffix(const Path& seed_path, const string& query_piece, size_t extension_len) const +list GappedGraphAligner::extendAlignmentSuffix( + const Path& seed_path, const string& query_piece, size_t extension_len, AlignerSelector& alignerSelector) const { assert(seed_path.length() == 0); int32_t top_alignment_score = INT32_MIN; list top_paths_and_alignments - = aligner_.prefixAlign(seed_path, query_piece, extension_len, top_alignment_score); + = alignerSelector.prefixAlign(seed_path, query_piece, extension_len, top_alignment_score); for (PathAndAlignment& path_and_alignment : top_paths_and_alignments) { diff --git a/thirdparty/graph-tools-master/src/graphalign/GraphAlignment.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GraphAlignment.cpp old mode 100755 new mode 100644 similarity index 99% rename from thirdparty/graph-tools-master/src/graphalign/GraphAlignment.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GraphAlignment.cpp index 5f0573e..5280757 --- a/thirdparty/graph-tools-master/src/graphalign/GraphAlignment.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GraphAlignment.cpp @@ -217,5 +217,4 @@ std::ostream& operator<<(std::ostream& out, const GraphAlignment& graph_alignmen } return out; } - } diff --git a/thirdparty/graph-tools-master/src/graphalign/GraphAlignmentOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GraphAlignmentOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/GraphAlignmentOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/GraphAlignmentOperations.cpp diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/KmerIndex.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/KmerIndex.cpp new file mode 100644 index 0000000..ce2ea3a --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/KmerIndex.cpp @@ -0,0 +1,347 @@ +// +// GraphTools library +// Copyright 2017-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko , +// Peter Krusche +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "graphalign/KmerIndex.hh" + +#include +#include +#include +#include +#include + +#include +#include + +#include "graphcore/PathOperations.hh" +#include "graphutils/KmerEncoding.hh" +#include "graphutils/PairHashing.hh" +#include "graphutils/SequenceOperations.hh" + +using std::list; +using std::string; +using std::unordered_set; +using std::vector; + +namespace graphtools +{ + +struct MiniPath +{ + bool operator==(const MiniPath& other) const + { + return (start_position == other.start_position) and (node_id == other.node_id); + } + + using pos_t = uint16_t; + using node_t = uint16_t; + pos_t start_position; + node_t node_id; +}; + +using KmerKey_t = uint32_t; + +typedef spp::sparse_hash_map StringToMiniPathsMap; +typedef spp::sparse_hash_map> StringToPathsMap; + +struct KmerIndex::Impl +{ + explicit Impl(const Graph& graph, size_t kmer_len_); + void addKmerPathsStartingAtNode(NodeId node_id); + void addKmerPaths(const std::list& kmer_paths); + void updateKmerCounts(); + Path MiniPathToPath(const MiniPath& miniPath) const; + +private: + const Graph& graph_; + +public: + size_t kmer_len; + TwoBitKmerEncoder kmer_coder; + StringToMiniPathsMap kmer_to_minipaths_map; + StringToPathsMap kmer_to_paths_map; + std::unordered_map node_kmer_counts; + std::unordered_map edge_kmer_counts; +}; + +KmerIndex::Impl::Impl(const Graph& graph, size_t kmer_len_) + : graph_(graph) + , kmer_len(kmer_len_) + , kmer_coder(kmer_len) +{ + for (NodeId node_id = 0; node_id != graph.numNodes(); ++node_id) + { + addKmerPathsStartingAtNode(node_id); + } + updateKmerCounts(); +} + +void KmerIndex::Impl::addKmerPathsStartingAtNode(NodeId node_id) +{ + const string node_seq = graph_.nodeSeq(node_id); + vector node_list; + node_list.push_back(node_id); + for (size_t pos = 0; pos != node_seq.length(); ++pos) + { + Path path(&graph_, static_cast(pos), node_list, static_cast(pos)); + addKmerPaths(extendPathEnd(path, static_cast(kmer_len))); + } +} + +Path KmerIndex::Impl::MiniPathToPath(const MiniPath& miniPath) const +{ + return Path(&graph_, miniPath.start_position, { miniPath.node_id }, miniPath.start_position + kmer_len); +} + +void KmerIndex::Impl::addKmerPaths(const list& kmer_paths) +{ + for (const Path& kmer_path : kmer_paths) + { + vector expanded_sequences; + expandReferenceSequence(kmer_path.seq(), expanded_sequences); + for (const auto& expanded_kmer_seq : expanded_sequences) + { + const auto expanded_kmer_key = kmer_coder.encode(expanded_kmer_seq); + auto pathIter(kmer_to_paths_map.find(expanded_kmer_key)); + if (pathIter != kmer_to_paths_map.end()) + { + pathIter->second.push_back(kmer_path); + continue; + } + + auto miniPathIter(kmer_to_minipaths_map.find(expanded_kmer_key)); + if (miniPathIter != kmer_to_minipaths_map.end()) + { + kmer_to_paths_map[expanded_kmer_key] = { MiniPathToPath(miniPathIter->second), kmer_path }; + kmer_to_minipaths_map.erase(miniPathIter); + } + else + { + if ((kmer_path.numNodes() == 1) + and ((kmer_path.endPosition() - kmer_path.startPosition()) == static_cast(kmer_len)) + and (kmer_path.startPosition() >= 0) + and (kmer_path.startPosition() <= std::numeric_limits::max()) + and (kmer_path.nodeIds().front() <= std::numeric_limits::max())) + { + MiniPath miniPath{ static_cast(kmer_path.startPosition()), + static_cast(kmer_path.nodeIds().front()) }; + kmer_to_minipaths_map.emplace(expanded_kmer_key, miniPath); + continue; + } + else + { + kmer_to_paths_map[expanded_kmer_key] = { kmer_path }; + } + } + } + } +} + +void KmerIndex::Impl::updateKmerCounts() +{ + node_kmer_counts.clear(); + edge_kmer_counts.clear(); + for (const auto& kmer_and_paths : kmer_to_minipaths_map) + { + auto const path_node_id(kmer_and_paths.second.node_id); + node_kmer_counts[path_node_id] += 1; + } + + for (const auto& kmer_and_paths : kmer_to_paths_map) + { + // kmer is unique + if (kmer_and_paths.second.size() == 1) + { + bool has_previous = false; + NodeId previous_node = 0; + for (auto const& path_node_id : kmer_and_paths.second.front().nodeIds()) + { + node_kmer_counts[path_node_id] += 1; + if (has_previous) + { + edge_kmer_counts[std::make_pair(previous_node, path_node_id)] += 1; + } + has_previous = true; + previous_node = path_node_id; + } + } + } +} + +KmerIndex::KmerIndex(const Graph& graph, int32_t kmer_len) + : pimpl_(new Impl(graph, static_cast(kmer_len))) +{ +} + +KmerIndex::KmerIndex(const KmerIndex& other) + : pimpl_(new Impl(*other.pimpl_)) +{ +} + +KmerIndex::KmerIndex(KmerIndex&& other) noexcept + : pimpl_(std::move(other.pimpl_)) +{ +} + +KmerIndex& KmerIndex::operator=(const KmerIndex& other) +{ + if (this != &other) + { + pimpl_.reset(new Impl(*other.pimpl_)); + } + return *this; +} + +KmerIndex& KmerIndex::operator=(KmerIndex&& other) noexcept +{ + pimpl_ = std::move(other.pimpl_); + return *this; +} + +KmerIndex::~KmerIndex() = default; + +bool KmerIndex::operator==(const KmerIndex& other) const +{ + return ( + pimpl_->kmer_to_minipaths_map == other.pimpl_->kmer_to_minipaths_map + and pimpl_->kmer_to_paths_map == other.pimpl_->kmer_to_paths_map && pimpl_->kmer_len == other.pimpl_->kmer_len); +} + +static string encodePaths(const std::vector& paths) +{ + std::vector path_encodings; + for (const auto& path : paths) + { + path_encodings.push_back(path.encode()); + } + return boost::algorithm::join(path_encodings, ","); +} + +size_t KmerIndex::kmerLength() const { return pimpl_->kmer_len; } + +string KmerIndex::encode() const +{ + std::vector kv_encodings; + for (const auto& kv : pimpl_->kmer_to_minipaths_map) + { + const string encoding_of_paths = encodePaths({ pimpl_->MiniPathToPath(kv.second) }); + kv_encodings.emplace_back("{" + pimpl_->kmer_coder.decode(kv.first) + "->" + encoding_of_paths + "}"); + } + for (const auto& kv : pimpl_->kmer_to_paths_map) + { + const string encoding_of_paths = encodePaths(kv.second); + kv_encodings.emplace_back("{" + pimpl_->kmer_coder.decode(kv.first) + "->" + encoding_of_paths + "}"); + } + return boost::algorithm::join(kv_encodings, ","); +} + +bool KmerIndex::contains(const std::string& kmer) const +{ + if ((kmer.size() != pimpl_->kmer_len) or (kmer.find_last_not_of("ACGT") != std::string::npos)) + { + return false; + } + + const auto kmer_key = pimpl_->kmer_coder.encode(kmer); + return ( + (pimpl_->kmer_to_minipaths_map.find(kmer_key) != pimpl_->kmer_to_minipaths_map.end()) + or (pimpl_->kmer_to_paths_map.find(kmer_key) != pimpl_->kmer_to_paths_map.end())); +} + +size_t KmerIndex::numPaths(const std::string& kmer) const +{ + if ((kmer.size() != pimpl_->kmer_len) or (kmer.find_last_not_of("ACGT") != std::string::npos)) + { + return 0; + } + + const auto kmer_key = pimpl_->kmer_coder.encode(kmer); + if (pimpl_->kmer_to_minipaths_map.find(kmer_key) != pimpl_->kmer_to_minipaths_map.end()) + { + return 1; + } + else + { + const auto pathIter(pimpl_->kmer_to_paths_map.find(kmer_key)); + if (pathIter == pimpl_->kmer_to_paths_map.end()) + { + return 0; + } + else + { + return pathIter->second.size(); + } + } +} + +std::vector KmerIndex::getPaths(const std::string& kmer) const +{ + const auto kmer_key = pimpl_->kmer_coder.encode(kmer); + const auto miniPathIter(pimpl_->kmer_to_minipaths_map.find(kmer_key)); + if (miniPathIter != pimpl_->kmer_to_minipaths_map.end()) + { + return { pimpl_->MiniPathToPath(miniPathIter->second) }; + } + else + { + return pimpl_->kmer_to_paths_map.at(kmer_key); + } +} + +unordered_set KmerIndex::kmers() const +{ + unordered_set kmers; + for (const auto& kv : pimpl_->kmer_to_minipaths_map) + { + kmers.insert(pimpl_->kmer_coder.decode(kv.first)); + } + for (const auto& kv : pimpl_->kmer_to_paths_map) + { + kmers.insert(pimpl_->kmer_coder.decode(kv.first)); + } + return kmers; +} + +size_t KmerIndex::numUniqueKmersOverlappingNode(NodeId node_id) const +{ + auto node_it = pimpl_->node_kmer_counts.find(node_id); + if (node_it != pimpl_->node_kmer_counts.end()) + { + return node_it->second; + } + return 0; +} + +size_t KmerIndex::numUniqueKmersOverlappingEdge(NodeId from, NodeId to) const +{ + auto edge_it = pimpl_->edge_kmer_counts.find(std::make_pair(from, to)); + if (edge_it != pimpl_->edge_kmer_counts.end()) + { + return edge_it->second; + } + return 0; +} + +std::ostream& operator<<(std::ostream& os, const KmerIndex& kmer_index) +{ + os << kmer_index.encode(); + return os; +} +} diff --git a/thirdparty/graph-tools-master/src/graphalign/KmerIndexOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/KmerIndexOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/KmerIndexOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/KmerIndexOperations.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/LinearAlignment.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/LinearAlignment.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/LinearAlignment.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/LinearAlignment.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/LinearAlignmentOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/LinearAlignmentOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/LinearAlignmentOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/LinearAlignmentOperations.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/Operation.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/Operation.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/Operation.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/Operation.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/OperationOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/OperationOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/OperationOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/OperationOperations.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/PinnedAligner.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/PinnedAligner.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/PinnedAligner.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/PinnedAligner.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/TracebackMatrix.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/TracebackMatrix.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/TracebackMatrix.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/TracebackMatrix.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/TracebackRunner.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/TracebackRunner.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/TracebackRunner.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/TracebackRunner.cpp diff --git a/thirdparty/graph-tools-master/src/graphalign/dagAligner/PenaltyMatrix.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/dagAligner/PenaltyMatrix.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphalign/dagAligner/PenaltyMatrix.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphalign/dagAligner/PenaltyMatrix.cpp diff --git a/thirdparty/graph-tools-master/src/graphcore/Graph.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Graph.cpp old mode 100755 new mode 100644 similarity index 94% rename from thirdparty/graph-tools-master/src/graphcore/Graph.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Graph.cpp index 46c2826..bdeabe3 --- a/thirdparty/graph-tools-master/src/graphcore/Graph.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Graph.cpp @@ -77,7 +77,7 @@ void Graph::setNodeName(NodeId node_id, const std::string& node_name) nodes_[node_id].name = node_name; } -const string& Graph::nodeSeq(NodeId node_id) const +const string& Graph::nodeSeq(const NodeId node_id) const { assertNodeExists(node_id); return nodes_[node_id].sequence; @@ -94,14 +94,7 @@ void Graph::setNodeSeq(NodeId node_id, const string& sequence) assertNodeExists(node_id); assertValidSequence(sequence); nodes_[node_id].sequence = sequence; - if (is_sequence_expansion_required_) - { - expandReferenceSequence(sequence, nodes_[node_id].sequence_expansion); - } - else - { - nodes_[node_id].sequence_expansion = { sequence }; - } + expandReferenceSequence(sequence, nodes_[node_id].sequence_expansion); } void Graph::addEdge(NodeId source_id, NodeId sink_id) diff --git a/thirdparty/graph-tools-master/src/graphcore/GraphBuilders.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphBuilders.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/GraphBuilders.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphBuilders.cpp diff --git a/thirdparty/graph-tools-master/src/graphcore/GraphCoordinates.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphCoordinates.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/GraphCoordinates.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphCoordinates.cpp diff --git a/thirdparty/graph-tools-master/src/graphcore/GraphOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphOperations.cpp old mode 100755 new mode 100644 similarity index 95% rename from thirdparty/graph-tools-master/src/graphcore/GraphOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphOperations.cpp index 9504c0a..54357ef --- a/thirdparty/graph-tools-master/src/graphcore/GraphOperations.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphOperations.cpp @@ -32,7 +32,7 @@ namespace graphtools */ Graph reverseGraph(Graph const& graph, bool complement) { - Graph reversed(graph.numNodes(), "", graph.isSequenceExpansionRequired()); + Graph reversed(graph.numNodes(), ""); for (NodeId node_id = 0; node_id != graph.numNodes(); ++node_id) { diff --git a/thirdparty/graph-tools-master/src/graphcore/GraphReferenceMapping.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphReferenceMapping.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/GraphReferenceMapping.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/GraphReferenceMapping.cpp diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Path.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Path.cpp new file mode 100644 index 0000000..7043175 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/Path.cpp @@ -0,0 +1,496 @@ +// +// GraphTools library +// Copyright 2017-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Egor Dolzhenko +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "graphcore/Path.hh" + +#include +#include +#include + +using std::logic_error; +using std::ostream; +using std::string; +using std::to_string; +using std::vector; + +namespace graphtools +{ + +void Path::assertValidity() const +{ + assertNonEmpty(); + assertFirstNodePosValid(); + assertLastNodePosValid(); + assertPositionsOrdered(); + assertConnected(); +} + +void Path::assertPositionsOrdered() const +{ + const bool positionsOrdered = nodes_.size() != 1 || start_position_ <= end_position_; + if (!positionsOrdered) + { + throw logic_error("Positions are not ordered"); + } +} + +bool Path::isNodePositionValid(const NodeId node_id, const int32_t position) const +{ + if (position < 0) + { + return false; + } + const string& node_seq = graph_raw_ptr_->nodeSeq(node_id); + return (unsigned)position <= node_seq.length(); +} + +void Path::assertNonEmpty() const +{ + if (nodes_.empty()) + { + throw logic_error("Path is empty"); + } +} + +void Path::assertFirstNodePosValid() const +{ + const NodeId first_node_id = nodes_.front(); + if (!isNodePositionValid(first_node_id, start_position_)) + { + throw logic_error("Position of first node is invalid"); + } +} + +void Path::assertLastNodePosValid() const +{ + const NodeId last_node_id = nodes_.back(); + if (!isNodePositionValid(last_node_id, end_position_)) + { + throw logic_error("Position of last node is invalid"); + } +} + +void Path::assertConnected() const +{ + const unsigned nodeCount(nodes_.size()); + for (unsigned nodeIndex(0); (nodeIndex + 1) < nodeCount; ++nodeIndex) + { + if (!graph_raw_ptr_->hasEdge(nodes_[nodeIndex], nodes_[nodeIndex + 1])) + { + throw logic_error("Path is not connected"); + } + } +} + +void Path::assertThatIndexIsValid(const int32_t node_index) const +{ + if (node_index < 0 || node_index >= (signed)nodes_.size()) + { + const string msg = "Node index " + to_string(node_index) + "is out of bounds for path " + encode(); + throw std::logic_error(msg); + } +} + +string Path::encode() const +{ + string path_encoding; + + size_t node_index = 0; + const size_t last_index = nodes_.size() - 1; + for (NodeId node_id : nodes_) + { + const string node_name = to_string(node_id); + string node_encoding; + if (node_index == 0) // Encoding first node. + { + node_encoding = "(" + node_name + "@" + to_string(start_position_) + ")"; + } + if (node_index == last_index) // Encoding last node. + { + node_encoding += "-(" + node_name + "@" + to_string(end_position_) + ")"; + } + if (node_index != 0 && node_index != last_index) // Encoding intermediate node. + { + node_encoding = "-(" + node_name + ")"; + } + path_encoding += node_encoding; + ++node_index; + } + + return path_encoding; +} + +Path::Path(const Graph* graph_raw_ptr, int32_t start_position, const vector& nodes, int32_t end_position) + : graph_raw_ptr_(graph_raw_ptr) + , start_position_(start_position) + , end_position_(end_position) + , nodes_(nodes) +{ + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to create path " + encode() + ": " + e.what()); + } +} + +bool Path::checkOverlapWithNode(const NodeId node_id) const +{ + return std::find(nodes_.begin(), nodes_.end(), node_id) != nodes_.end(); +} + +int32_t Path::getStartPositionOnNodeByIndex(size_t node_index) const +{ + assertThatIndexIsValid(static_cast(node_index)); + + if (node_index == 0) + { + return startPosition(); + } + + return 0; +} + +int32_t Path::getEndPositionOnNodeByIndex(size_t node_index) const +{ + assertThatIndexIsValid(static_cast(node_index)); + + if (node_index == (numNodes() - 1)) + { + return endPosition(); + } + + return graphRawPtr()->nodeSeq(nodes_[node_index]).length(); +} + +size_t Path::getNodeOverlapLengthByIndex(size_t node_index) const +{ + assertThatIndexIsValid(static_cast(node_index)); + const size_t node_length = graphRawPtr()->nodeSeq(nodes_[node_index]).length(); + auto length_on_node = (int32_t)node_length; // This is the length of all intermediate nodes. + + const bool is_first_node = node_index == 0; + const bool is_last_node = node_index + 1 == numNodes(); + + if (is_first_node && is_last_node) + { + length_on_node = end_position_ - start_position_; + } + else if (is_first_node) + { + length_on_node = static_cast(node_length - start_position_); + } + else if (is_last_node) + { + length_on_node = end_position_; + } + + return static_cast(length_on_node); +} + +int32_t Path::getDistanceFromPathStart(const NodeId node, const int32_t offset) const +{ + int32_t distance = 0; + bool found = false; + const size_t nodeCount(numNodes()); + for (size_t nodeIndex(0); nodeIndex < nodeCount; ++nodeIndex) + { + const auto node_id = nodes_[nodeIndex]; + const int32_t node_start = nodeIndex == 0 ? start_position_ : 0; + const int32_t node_end + = (nodeIndex + 1) == nodeCount ? end_position_ : (int32_t)graph_raw_ptr_->nodeSeq(node_id).size() - 1; + + if (node_id == node && offset >= node_start && offset <= node_end) + { + distance += offset - node_start; + found = true; + break; + } + + distance += node_end - node_start + 1; + } + if (!found) + { + throw std::logic_error(std::to_string(node) + "@" + std::to_string(offset) + " is not on path " + encode()); + } + return distance; +} + +size_t Path::length() const +{ + size_t path_length = 0; + const size_t nodeCount(numNodes()); + for (size_t node_index = 0; node_index != nodeCount; ++node_index) + { + path_length += getNodeOverlapLengthByIndex(node_index); + } + + return path_length; +} + +string Path::getNodeSeq(const size_t node_index) const +{ + const string& sequence = graph_raw_ptr_->nodeSeq(nodes_[node_index]); + + if (node_index == 0) + { + const size_t node_overlap_len = getNodeOverlapLengthByIndex(node_index); + return sequence.substr(static_cast(start_position_), node_overlap_len); + } + else if ((node_index + 1) == numNodes()) + { + const size_t node_overlap_len = getNodeOverlapLengthByIndex(node_index); + return sequence.substr(0, node_overlap_len); + } + else + { + return sequence; + } +} + +string Path::seq() const +{ + string path_seq; + const size_t nodeCount(numNodes()); + for (size_t node_index = 0; node_index != nodeCount; ++node_index) + { + const std::string& node_seq(graph_raw_ptr_->nodeSeq(nodes_[node_index])); + if ((node_index == 0) or ((node_index + 1) == nodeCount)) + { + const size_t pos((node_index == 0) ? start_position_ : 0); + const size_t len(((node_index + 1) == nodeCount) ? (end_position_ - pos) : std::string::npos); + path_seq += node_seq.substr(pos, len); + } + else + { + path_seq += node_seq; + } + } + return path_seq; +} + +ostream& operator<<(ostream& os, const Path& path) { return os << path.encode(); } + +void Path::shiftStartAlongNode(const int32_t shift_len) +{ + start_position_ -= shift_len; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to shift start of " + encode() + " by " + to_string(shift_len) + ": " + e.what()); + } +} + +void Path::shiftEndAlongNode(const int32_t shift_len) +{ + end_position_ += shift_len; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to shift end of " + encode() + " by " + to_string(shift_len) + ": " + e.what()); + } +} + +void Path::extendStartToNode(const NodeId node_id) +{ + nodes_.insert(nodes_.begin(), node_id); + const auto new_node_seq_len = static_cast(graph_raw_ptr_->nodeSeq(node_id).length()); + start_position_ = new_node_seq_len; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to extend " + encode() + " to node " + to_string(node_id) + ": " + e.what()); + } +} + +void Path::extendStartToIncludeNode(const NodeId node_id) +{ + nodes_.insert(nodes_.begin(), node_id); + start_position_ = 0; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to extend " + encode() + " to node " + to_string(node_id) + ": " + e.what()); + } +} + +void Path::removeStartNode() +{ + nodes_.erase(nodes_.begin()); + start_position_ = 0; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to remove start node of " + encode() + ": " + e.what()); + } +} + +void Path::removeZeroLengthStart() +{ + if (numNodes() > 1 && getNodeOverlapLengthByIndex(0) == 0) + { + removeStartNode(); + } +} + +void Path::removeZeroLengthEnd() +{ + const size_t index_of_last_node = numNodes() - 1; + if (numNodes() > 1 && getNodeOverlapLengthByIndex(index_of_last_node) == 0) + { + removeEndNode(); + } +} + +void Path::extendEndToNode(const NodeId node_id) +{ + nodes_.push_back(node_id); + end_position_ = 0; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to extend " + encode() + " right to node " + to_string(node_id) + ": " + e.what()); + } +} + +void Path::extendEndToIncludeNode(const NodeId node_id) +{ + nodes_.push_back(node_id); + const auto new_node_seq_len = static_cast(graph_raw_ptr_->nodeSeq(node_id).length()); + end_position_ = new_node_seq_len; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to extend " + encode() + " right to node " + to_string(node_id) + ": " + e.what()); + } +} + +void Path::removeEndNode() +{ + nodes_.erase(nodes_.end() - 1); + NodeId new_last_node_id = nodes_.back(); + auto new_last_node_len = static_cast(graph_raw_ptr_->nodeSeq(new_last_node_id).length()); + end_position_ = new_last_node_len; + + try + { + assertValidity(); + } + catch (const std::exception& e) + { + throw logic_error("Unable to remove end node of " + encode() + ": " + e.what()); + } +} + +void Path::shrinkStartBy(const int32_t shrink_len) +{ + const int32_t node_len_left = getNodeOverlapLengthByIndex(0); + + if (shrink_len <= node_len_left) + { + shiftStartAlongNode(-shrink_len); + removeZeroLengthStart(); + } + else + { + removeStartNode(); + + const int32_t leftover_len = shrink_len - node_len_left; + shrinkStartBy(leftover_len); + } +} + +void Path::shrinkEndBy(const int32_t shrink_len) +{ + const int32_t node_len_left = end_position_; + + if (shrink_len <= node_len_left) + { + shiftEndAlongNode(-shrink_len); + removeZeroLengthEnd(); + } + else + { + removeEndNode(); + + const int32_t leftover_len = shrink_len - node_len_left; + shrinkEndBy(leftover_len); + } +} + +void Path::shrinkBy(const int32_t start_shrink_len, const int32_t end_shrink_len) +{ + shrinkStartBy(start_shrink_len); + shrinkEndBy(end_shrink_len); +} + +bool Path::operator==(const Path& other) const +{ + return (graph_raw_ptr_ == other.graph_raw_ptr_) && (start_position_ == other.start_position_) + && (end_position_ == other.end_position_) && (nodes_ == other.nodes_); +} + +bool Path::operator<(const Path& other) const +{ + if (start_position_ != other.start_position_) + { + return start_position_ < other.start_position_; + } + + if (nodes_ != other.nodes_) + { + return nodes_ < other.nodes_; + } + + return end_position_ < other.end_position_; +} + +std::ostream& operator<<(std::ostream& os, const ReversePath& path) { return os << "reverse path of: " << path.path_; } +} diff --git a/thirdparty/graph-tools-master/src/graphcore/PathFamily.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathFamily.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/PathFamily.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathFamily.cpp diff --git a/thirdparty/graph-tools-master/src/graphcore/PathFamilyOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathFamilyOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/PathFamilyOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathFamilyOperations.cpp diff --git a/thirdparty/graph-tools-master/src/graphcore/PathOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphcore/PathOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphcore/PathOperations.cpp diff --git a/thirdparty/graph-tools-master/src/graphio/AlignmentWriter.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphio/AlignmentWriter.cpp old mode 100755 new mode 100644 similarity index 99% rename from thirdparty/graph-tools-master/src/graphio/AlignmentWriter.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphio/AlignmentWriter.cpp index 15eacab..e457f32 --- a/thirdparty/graph-tools-master/src/graphio/AlignmentWriter.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphio/AlignmentWriter.cpp @@ -36,5 +36,4 @@ void BlankAlignmentWriter::write( bool /*isFirstMate*/, bool /*isReversed*/, bool /*isMateReversed*/, const GraphAlignment& /*alignment*/) { } - } diff --git a/thirdparty/graph-tools-master/src/graphio/GraphJson.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphio/GraphJson.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphio/GraphJson.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphio/GraphJson.cpp diff --git a/thirdparty/graph-tools-master/src/graphutils/DepthTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/DepthTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphutils/DepthTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/DepthTest.cpp diff --git a/thirdparty/graph-tools-master/src/graphutils/IntervalBuffer.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/IntervalBuffer.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphutils/IntervalBuffer.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/IntervalBuffer.cpp diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/KmerEncoding.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/KmerEncoding.cpp new file mode 100644 index 0000000..d2956cd --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/KmerEncoding.cpp @@ -0,0 +1,27 @@ +// +// GraphTools library +// Copyright 2017-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "graphutils/KmerEncoding.hh" + +namespace graphtools +{ + +TwoBitKmerEncoder::BaseToIndex TwoBitKmerEncoder::baseToIndex_; +} diff --git a/thirdparty/graph-tools-master/src/graphutils/SequenceOperations.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/SequenceOperations.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/graphutils/SequenceOperations.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/graphutils/SequenceOperations.cpp diff --git a/thirdparty/graph-tools-master/src/sh/check-format.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/check-format.sh similarity index 94% rename from thirdparty/graph-tools-master/src/sh/check-format.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/check-format.sh index 4091afb..9850365 100755 --- a/thirdparty/graph-tools-master/src/sh/check-format.sh +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/check-format.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -SOURCE_DIR="${DIR}/../.." +SOURCE_DIR="${DIR}/../../src" export CLANG_FORMAT=clang-format-5.0 set +e @@ -20,4 +20,4 @@ if [[ ${DIFFS} == 0 ]]; then else echo "Some formatting issues were found." exit 1 -fi \ No newline at end of file +fi diff --git a/thirdparty/graph-tools-master/src/sh/docker-build.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-build.sh similarity index 100% rename from thirdparty/graph-tools-master/src/sh/docker-build.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-build.sh diff --git a/thirdparty/graph-tools-master/src/sh/docker-check-format.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-check-format.sh similarity index 100% rename from thirdparty/graph-tools-master/src/sh/docker-check-format.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-check-format.sh diff --git a/thirdparty/graph-tools-master/src/sh/docker-cppcheck.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-cppcheck.sh similarity index 100% rename from thirdparty/graph-tools-master/src/sh/docker-cppcheck.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-cppcheck.sh diff --git a/thirdparty/graph-tools-master/src/sh/docker-format-everything.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-format-everything.sh similarity index 100% rename from thirdparty/graph-tools-master/src/sh/docker-format-everything.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/docker-format-everything.sh diff --git a/thirdparty/graph-tools-master/src/sh/format-everything.sh b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/format-everything.sh similarity index 100% rename from thirdparty/graph-tools-master/src/sh/format-everything.sh rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/format-everything.sh diff --git a/thirdparty/graph-tools-master/src/sh/valgrind-check.py b/ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/valgrind-check.py old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/src/sh/valgrind-check.py rename to ehunter/thirdparty/graph-tools-master-f421f4c/src/sh/valgrind-check.py diff --git a/thirdparty/graph-tools-master/tests/BaseMatchingTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/BaseMatchingTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/BaseMatchingTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/BaseMatchingTest.cpp diff --git a/thirdparty/graph-tools-master/tests/CMakeLists.txt b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/CMakeLists.txt old mode 100755 new mode 100644 similarity index 96% rename from thirdparty/graph-tools-master/tests/CMakeLists.txt rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/CMakeLists.txt index 00eae27..a3e73e4 --- a/thirdparty/graph-tools-master/tests/CMakeLists.txt +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/CMakeLists.txt @@ -66,6 +66,10 @@ add_executable(PathOperationsTest PathOperationsTest.cpp) target_link_libraries(PathOperationsTest graphtools gtest_main) add_test(NAME PathOperationsTest COMMAND PathOperationsTest) +add_executable(KmerEncodingTest KmerEncodingTest.cpp) +target_link_libraries(KmerEncodingTest graphtools gtest_main) +add_test(NAME KmerEncodingTest COMMAND KmerEncodingTest) + add_executable(KmerIndexTest KmerIndexTest.cpp) target_link_libraries(KmerIndexTest graphtools gtest_main) add_test(NAME KmerIndexTest COMMAND KmerIndexTest) diff --git a/thirdparty/graph-tools-master/tests/DagAlignerTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/DagAlignerTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/DagAlignerTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/DagAlignerTest.cpp diff --git a/thirdparty/graph-tools-master/tests/DepthTestTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/DepthTestTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/DepthTestTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/DepthTestTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GappedAlignerTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GappedAlignerTest.cpp old mode 100755 new mode 100644 similarity index 80% rename from thirdparty/graph-tools-master/tests/GappedAlignerTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GappedAlignerTest.cpp index b2afc7a..4bbc449 --- a/thirdparty/graph-tools-master/tests/GappedAlignerTest.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GappedAlignerTest.cpp @@ -41,7 +41,8 @@ TEST(DISABLED_ExtendingAlignmentSuffix, UniquelyMappingQuery_AlignmentExtended) const size_t kmer_len = 3; const size_t padding_len = 5; const int32_t seed_affix_trim_len = 0; - GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len, "path-aligner"); + GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len); + AlignerSelector alignerSelector(AlignerType::PATH_ALIGNER); // -> CGCGCGTA // | |||||| @@ -50,7 +51,8 @@ TEST(DISABLED_ExtendingAlignmentSuffix, UniquelyMappingQuery_AlignmentExtended) Path seed_path(&graph, 3, { 0 }, 3); const size_t extension_len = 12; - list extensions = aligner.extendAlignmentSuffix(seed_path, "CCGCGTA", extension_len); + list extensions + = aligner.extendAlignmentSuffix(seed_path, "CCGCGTA", extension_len, alignerSelector); Alignment expected_alignment(0, "1M1D6M"); Path expected_path(&graph, 3, { 0, 1, 1, 1, 2 }, 2); @@ -66,10 +68,11 @@ TEST(DISABLED_ExtendingAlignmentSuffix, MultiMappingQuery_AlignmentExtended) const size_t kmer_len = 3; const size_t padding_len = 0; const int32_t seed_affix_trim_len = 0; - GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len, "path-aligner"); + GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len); + AlignerSelector alignerSelector(AlignerType::PATH_ALIGNER); Path seed_path(&graph, 3, { 0 }, 3); - list extensions = aligner.extendAlignmentSuffix(seed_path, "CCC", 3); + list extensions = aligner.extendAlignmentSuffix(seed_path, "CCC", 3, alignerSelector); Alignment expected_alignment_a = Alignment(0, "3M"); Path expected_path_a(&graph, 3, { 0, 1, 1, 1 }, 1); @@ -87,7 +90,7 @@ TEST(DISABLED_ExtendingAlignmentSuffix, MultiMappingQuery_AlignmentExtended) EXPECT_EQ(expected_extensions, extensions); } -class AlignerTests : public ::testing::TestWithParam +class AlignerTests : public ::testing::TestWithParam { }; @@ -99,7 +102,8 @@ TEST_P(AlignerTests, ExtendingAlignmentPrefix_TypicalSequences_AlignmentExtended const size_t kmer_len = 3; const size_t padding_len = 5; const size_t seed_affix_trim_len = 0; - GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len, GetParam()); + GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len); + AlignerSelector alignerSelector(GetParam()); // ATTAC-GCGC <- // || || ||| @@ -108,7 +112,8 @@ TEST_P(AlignerTests, ExtendingAlignmentPrefix_TypicalSequences_AlignmentExtended Path seed_path(&graph, 1, { 1 }, 1); const size_t extension_len = 10; - list extensions = aligner.extendAlignmentPrefix(seed_path, "ATAACAGCGG", extension_len); + list extensions + = aligner.extendAlignmentPrefix(seed_path, "ATAACAGCGG", extension_len, alignerSelector); Alignment expected_alignment(0, "2M1X2M1I3M1X"); Path expected_path(&graph, 2, { 0, 1, 1, 1 }, 1); @@ -124,15 +129,15 @@ TEST_P(AlignerTests, PerformingGappedAlignment_UniquelyMappingQuery_AlignmentPer const size_t kmer_len = 3; const size_t padding_len = 2; const int32_t seed_affix_trim_len = 0; - GappedGraphAligner aligner( - &graph, kmer_len, padding_len, seed_affix_trim_len, GetParam(), LinearAlignmentParameters(5, -4, -8, 0)); + GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len); + AlignerSelector alignerSelector(GetParam(), LinearAlignmentParameters(5, -4, -8, 0)); { // TTA-CG-CG-TAT // || || | ||| // TT--CG-C--TAT - list alignments = aligner.align("TTCGCTAT"); + list alignments = aligner.align("TTCGCTAT", alignerSelector); list expected_alignments = { decodeGraphAlignment(3, "0[2M1D]1[2M]1[1M1D]2[3M]", &graph) }; // with default m=5,mm=-4,go=-8,ge=-2 2M1D=10-8-2=0, 1M1X=5-4=1, so, test needs an update: @@ -141,7 +146,7 @@ TEST_P(AlignerTests, PerformingGappedAlignment_UniquelyMappingQuery_AlignmentPer } { - list alignments = aligner.align("ATTCGCTAT"); + list alignments = aligner.align("ATTCGCTAT", alignerSelector); list expected_alignments = { decodeGraphAlignment(2, "0[3M1D]1[2M]1[1M1D]2[3M]", &graph) }; // with default m=5,mm=-4,go=-8,ge=-2 2M1D=10-8-2=0, 1M1X=5-4=1, so, test needs an update: @@ -153,13 +158,14 @@ TEST_P(AlignerTests, PerformingGappedAlignment_UniquelyMappingQuery_AlignmentPer TEST_P(AlignerTests, PerformingGappedAlignment_MultimappingQuery_BestAlignmentsComputed) { Graph graph = makeStrGraph("AAG", "CGG", "CTT"); - GappedGraphAligner aligner(&graph, 3, 0, 0, GetParam()); + GappedGraphAligner aligner(&graph, 3, 0, 0); + AlignerSelector alignerSelector(GetParam()); // G-CG-C // 0-11-1 // 1-11-1 - list alignments = aligner.align("GCGGC"); + list alignments = aligner.align("GCGGC", alignerSelector); list expected_alignments = { decodeGraphAlignment(2, "0[1M]1[3M]1[1M]", &graph), decodeGraphAlignment(2, "0[1M]1[3M]2[1M]", &graph), @@ -170,22 +176,23 @@ TEST_P(AlignerTests, PerformingGappedAlignment_MultimappingQuery_BestAlignmentsC TEST_P(AlignerTests, PerformingGappedAlignment_KmerExtensionInBothDirectionsNotNeeded_BestAlignmentsComputed) { Graph graph = makeStrGraph("AAG", "CGG", "CTT"); - GappedGraphAligner aligner(&graph, 3, 0, 0, GetParam()); + GappedGraphAligner aligner(&graph, 3, 0, 0); + AlignerSelector alignerSelector(GetParam()); { - list alignments = aligner.align("CGGCT"); + list alignments = aligner.align("CGGCT", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[3M]2[2M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } { - list alignments = aligner.align("AATCGG"); + list alignments = aligner.align("AATCGG", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "0[2M1X]1[3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } { - list alignments = aligner.align("CTT"); + list alignments = aligner.align("CTT", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "2[3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } @@ -194,22 +201,23 @@ TEST_P(AlignerTests, PerformingGappedAlignment_KmerExtensionInBothDirectionsNotN TEST_P(AlignerTests, PerformingGappedAlignment_KmerExtensionIsUnalignable_BestAlignmentsComputed) { Graph graph = makeStrGraph("AAG", "CGG", "CTT"); - GappedGraphAligner aligner(&graph, 3, 0, 0, GetParam()); + GappedGraphAligner aligner(&graph, 3, 0, 0); + AlignerSelector alignerSelector(GetParam()); { - list alignments = aligner.align("CGGAA"); + list alignments = aligner.align("CGGAA", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[3M2S]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } { - list alignments = aligner.align("TTCGG"); + list alignments = aligner.align("TTCGG", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[2S3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } { - list alignments = aligner.align("TCGGA"); + list alignments = aligner.align("TCGGA", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[1S3M1S]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } @@ -218,9 +226,10 @@ TEST_P(AlignerTests, PerformingGappedAlignment_KmerExtensionIsUnalignable_BestAl TEST_P(AlignerTests, PerformingGappedAlignment_PolyalanineRepeat_ReadAligned) { Graph graph = makeStrGraph("AAG", "GCN", "ATT"); - GappedGraphAligner aligner(&graph, 4, 0, 0, GetParam()); + GappedGraphAligner aligner(&graph, 4, 0, 0); + AlignerSelector alignerSelector(GetParam()); - list alignments = aligner.align("AGGCCGTGGCAATT"); + list alignments = aligner.align("AGGCCGTGGCAATT", alignerSelector); list expected_alignments = { decodeGraphAlignment(1, "0[2M]1[3M]1[1M1X1M]1[3M]2[3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); @@ -229,9 +238,10 @@ TEST_P(AlignerTests, PerformingGappedAlignment_PolyalanineRepeat_ReadAligned) TEST_P(AlignerTests, PerformingGappedAlignment_ReadWithLowqualityBases_ReadAligned) { Graph graph = makeStrGraph("AAG", "CGG", "CTT"); - GappedGraphAligner aligner(&graph, 4, 0, 0, GetParam()); + GappedGraphAligner aligner(&graph, 4, 0, 0); + AlignerSelector alignerSelector(GetParam()); - list alignments = aligner.align("aagcggctt"); + list alignments = aligner.align("aagcggctt", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "0[3M]1[3M]2[3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); @@ -241,17 +251,18 @@ TEST_P(AlignerTests, PerformingGappedAlignment_IncorrectSeedKmer_ReadAligned) { Graph graph = makeStrGraph("AAAA", "CCG", "TTTT"); const int32_t seed_affix_trim_len = 2; - GappedGraphAligner aligner(&graph, 4, 0, seed_affix_trim_len, GetParam()); + GappedGraphAligner aligner(&graph, 4, 0, seed_affix_trim_len); + AlignerSelector alignerSelector(GetParam()); { - list alignments = aligner.align("CCACCGTTTT"); + list alignments = aligner.align("CCACCGTTTT", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[2M1X]1[3M]2[4M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } { - list alignments = aligner.align("CCGTCG"); + list alignments = aligner.align("CCGTCG", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[3M]1[1X2M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); @@ -296,25 +307,27 @@ TEST_P(AlignerTests, PerformingGappedAlignment_NoExceptionThrown) const size_t kmer_len = 14; const size_t padding_len = 10; const int32_t seed_affix_trim_len = 14; - GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len, GetParam()); + GappedGraphAligner aligner(&graph, kmer_len, padding_len, seed_affix_trim_len); + AlignerSelector alignerSelector(GetParam()); const string query = "ctTTttgaTTTtttccctcacatgTTTTTtatatGataTtTctcTtCtCtcataTAtttaTAtAtAttAtATtTAtAtataTctttTAtATAT" "AtaATaTaTaTATatCATATAtATaTATGATATATATATATATCATATATATATATG"; - ASSERT_NO_THROW(aligner.align(query)); + ASSERT_NO_THROW(aligner.align(query, alignerSelector)); } TEST_P(AlignerTests, PerformingGappedAlignment_FlankWithStrKmer_ReadAligned) { Graph graph = makeStrGraph("AAAA", "CGG", "TTCGGCGGTT"); const int32_t seed_affix_trim_len = 2; - GappedGraphAligner aligner(&graph, 4, 0, seed_affix_trim_len, GetParam()); + GappedGraphAligner aligner(&graph, 4, 0, seed_affix_trim_len); + AlignerSelector alignerSelector(GetParam()); - list alignments = aligner.align("CGGCGGCGGCGGCGG"); + list alignments = aligner.align("CGGCGGCGGCGGCGG", alignerSelector); list expected_alignments = { decodeGraphAlignment(0, "1[3M]1[3M]1[3M]1[3M]1[3M]", &graph) }; EXPECT_EQ(expected_alignments, alignments); } -INSTANTIATE_TEST_CASE_P( - AlignerTestsInst, AlignerTests, ::testing::Values(std::string("path-aligner"), std::string("dag-aligner")), ); +INSTANTIATE_TEST_SUITE_P( + AlignerTestsInst, AlignerTests, ::testing::Values(AlignerType::PATH_ALIGNER, AlignerType::DAG_ALIGNER)); diff --git a/thirdparty/graph-tools-master/tests/GraphAlignmentOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphAlignmentOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphAlignmentOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphAlignmentOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphAlignmentTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphAlignmentTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphAlignmentTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphAlignmentTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphBuildersTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphBuildersTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphBuildersTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphBuildersTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphCoordinatesTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphCoordinatesTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphCoordinatesTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphCoordinatesTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphJsonTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphJsonTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphJsonTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphJsonTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphReferenceMappingTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphReferenceMappingTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphReferenceMappingTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphReferenceMappingTest.cpp diff --git a/thirdparty/graph-tools-master/tests/GraphTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/GraphTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/GraphTest.cpp diff --git a/thirdparty/graph-tools-master/tests/IntervalBufferTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/IntervalBufferTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/IntervalBufferTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/IntervalBufferTest.cpp diff --git a/thirdparty/graph-tools-master/tests/IntervalListTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/IntervalListTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/IntervalListTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/IntervalListTest.cpp diff --git a/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerEncodingTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerEncodingTest.cpp new file mode 100644 index 0000000..8f20ce4 --- /dev/null +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerEncodingTest.cpp @@ -0,0 +1,36 @@ +// +// GraphTools library +// Copyright 2017-2019 Illumina, Inc. +// All rights reserved. +// +// Author: Chris Saunders +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "graphutils/KmerEncoding.hh" + +#include "gtest/gtest.h" + +using namespace graphtools; + +TEST(TwoBitKmerEncoder, 1TwoBitKmerEncoder) +{ + TwoBitKmerEncoder ke(4); + + ASSERT_EQ(ke.decode(ke.encode("TAAG")), "TAAG"); + ASSERT_EQ(ke.decode(ke.encode("CTAA")), "CTAA"); + + ASSERT_EQ(ke.encode(ke.decode(12)), 12); + ASSERT_EQ(ke.encode(ke.decode(0)), 0); +} diff --git a/thirdparty/graph-tools-master/tests/KmerIndexOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerIndexOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/KmerIndexOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerIndexOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/KmerIndexTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerIndexTest.cpp old mode 100755 new mode 100644 similarity index 67% rename from thirdparty/graph-tools-master/tests/KmerIndexTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerIndexTest.cpp index c978619..e7c2b22 --- a/thirdparty/graph-tools-master/tests/KmerIndexTest.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/KmerIndexTest.cpp @@ -41,14 +41,14 @@ TEST(KmerIndexInitialization, 1mers_IndexCreated) const int32_t kmer_size = 1; KmerIndex kmer_index(graph, kmer_size); - const list a_paths = { Path(&graph, 0, { 0 }, 1), Path(&graph, 1, { 2 }, 2) }; - const list c_paths = { Path(&graph, 1, { 0 }, 2), Path(&graph, 0, { 2 }, 1) }; - const list g_paths = { Path(&graph, 0, { 1 }, 1), Path(&graph, 1, { 1 }, 2), Path(&graph, 2, { 2 }, 3) }; - - const StringToPathsMap kmer_to_paths_maps = { { "A", a_paths }, { "C", c_paths }, { "G", g_paths } }; - - KmerIndex expected_kmer_index(kmer_to_paths_maps); - ASSERT_EQ(expected_kmer_index, kmer_index); + const std::vector expected_a_paths = { Path(&graph, 0, { 0 }, 1), Path(&graph, 1, { 2 }, 2) }; + const std::vector expected_c_paths = { Path(&graph, 1, { 0 }, 2), Path(&graph, 0, { 2 }, 1) }; + const std::vector expected_g_paths + = { Path(&graph, 0, { 1 }, 1), Path(&graph, 1, { 1 }, 2), Path(&graph, 2, { 2 }, 3) }; + + ASSERT_EQ(kmer_index.getPaths("A"), expected_a_paths); + ASSERT_EQ(kmer_index.getPaths("C"), expected_c_paths); + ASSERT_EQ(kmer_index.getPaths("G"), expected_g_paths); } TEST(KmerIndexInitialization, 2mers_IndexCreated) @@ -57,23 +57,24 @@ TEST(KmerIndexInitialization, 2mers_IndexCreated) const int32_t kmer_size = 2; KmerIndex kmer_index(graph, kmer_size); - const list ag_paths = { Path(&graph, 0, { 0 }, 2), Path(&graph, 1, { 2 }, 3) }; - const list at_paths = { Path(&graph, 0, { 0 }, 2) }; - - const list gg_paths = { Path(&graph, 1, { 0, 1 }, 1), Path(&graph, 0, { 1 }, 2) }; - const list tg_paths = { Path(&graph, 1, { 0, 1 }, 1) }; + const std::vector expected_ag_paths = { Path(&graph, 0, { 0 }, 2), Path(&graph, 1, { 2 }, 3) }; + const std::vector expected_at_paths = { Path(&graph, 0, { 0 }, 2) }; - const list gc_paths = { Path(&graph, 1, { 0, 2 }, 1), Path(&graph, 1, { 1, 2 }, 1) }; - const list tc_paths = { Path(&graph, 1, { 0, 2 }, 1) }; + const std::vector expected_gg_paths = { Path(&graph, 1, { 0, 1 }, 1), Path(&graph, 0, { 1 }, 2) }; + const std::vector expected_tg_paths = { Path(&graph, 1, { 0, 1 }, 1) }; - const list ca_paths = { Path(&graph, 0, { 2 }, 2) }; + const std::vector expected_gc_paths = { Path(&graph, 1, { 0, 2 }, 1), Path(&graph, 1, { 1, 2 }, 1) }; + const std::vector expected_tc_paths = { Path(&graph, 1, { 0, 2 }, 1) }; - const StringToPathsMap kmer_to_paths_maps - = { { "AG", ag_paths }, { "AT", at_paths }, { "GG", gg_paths }, { "TG", tg_paths }, - { "GC", gc_paths }, { "TC", tc_paths }, { "CA", ca_paths } }; + const std::vector expected_ca_paths = { Path(&graph, 0, { 2 }, 2) }; - KmerIndex expected_kmer_index(kmer_to_paths_maps); - ASSERT_EQ(expected_kmer_index, kmer_index); + ASSERT_EQ(kmer_index.getPaths("AG"), expected_ag_paths); + ASSERT_EQ(kmer_index.getPaths("AT"), expected_at_paths); + ASSERT_EQ(kmer_index.getPaths("GG"), expected_gg_paths); + ASSERT_EQ(kmer_index.getPaths("TG"), expected_tg_paths); + ASSERT_EQ(kmer_index.getPaths("GC"), expected_gc_paths); + ASSERT_EQ(kmer_index.getPaths("TC"), expected_tc_paths); + ASSERT_EQ(kmer_index.getPaths("CA"), expected_ca_paths); } TEST(KmerExtraction, TypicalIndex_KmersExtracted) @@ -90,8 +91,8 @@ TEST(PathExtraction, TypicalIndex_PathsExtracted) Graph graph = makeDoubleSwapGraph("AAA", "TTT", "CCC", "AAA", "TTT", "AAA", "TTT"); const int32_t kmer_size = 4; KmerIndex kmer_index(graph, kmer_size); - const list paths = kmer_index.getPaths("AATT"); - const list expected_paths + const std::vector paths = kmer_index.getPaths("AATT"); + const std::vector expected_paths = { Path(&graph, 1, { 0, 1 }, 2), Path(&graph, 1, { 3, 4 }, 2), Path(&graph, 1, { 5, 6 }, 2) }; ASSERT_EQ(expected_paths, paths); } diff --git a/thirdparty/graph-tools-master/tests/LinearAlignmentOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/LinearAlignmentOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/LinearAlignmentOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/LinearAlignmentOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/LinearAlignmentTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/LinearAlignmentTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/LinearAlignmentTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/LinearAlignmentTest.cpp diff --git a/thirdparty/graph-tools-master/tests/OperationOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/OperationOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/OperationOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/OperationOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/OperationTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/OperationTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/OperationTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/OperationTest.cpp diff --git a/thirdparty/graph-tools-master/tests/PathFamilyOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathFamilyOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/PathFamilyOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathFamilyOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/PathFamilyTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathFamilyTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/PathFamilyTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathFamilyTest.cpp diff --git a/thirdparty/graph-tools-master/tests/PathOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/PathOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/PathTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathTest.cpp old mode 100755 new mode 100644 similarity index 99% rename from thirdparty/graph-tools-master/tests/PathTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathTest.cpp index dd5e667..f87e62c --- a/thirdparty/graph-tools-master/tests/PathTest.cpp +++ b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PathTest.cpp @@ -25,7 +25,6 @@ #include "graphcore/Graph.hh" #include "graphcore/GraphBuilders.hh" -using std::list; using std::string; using std::vector; diff --git a/thirdparty/graph-tools-master/tests/PinnedAlignerTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PinnedAlignerTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/PinnedAlignerTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PinnedAlignerTest.cpp diff --git a/thirdparty/graph-tools-master/tests/PinnedDagAlignerTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/PinnedDagAlignerTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/PinnedDagAlignerTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/PinnedDagAlignerTest.cpp diff --git a/thirdparty/graph-tools-master/tests/SequenceOperationsTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/SequenceOperationsTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/SequenceOperationsTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/SequenceOperationsTest.cpp diff --git a/thirdparty/graph-tools-master/tests/TracebackMatrixTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/TracebackMatrixTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/TracebackMatrixTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/TracebackMatrixTest.cpp diff --git a/thirdparty/graph-tools-master/tests/TracebackRunnerTest.cpp b/ehunter/thirdparty/graph-tools-master-f421f4c/tests/TracebackRunnerTest.cpp old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/graph-tools-master/tests/TracebackRunnerTest.cpp rename to ehunter/thirdparty/graph-tools-master-f421f4c/tests/TracebackRunnerTest.cpp diff --git a/thirdparty/intervaltree/IntervalTree.h b/ehunter/thirdparty/intervaltree/IntervalTree.h similarity index 99% rename from thirdparty/intervaltree/IntervalTree.h rename to ehunter/thirdparty/intervaltree/IntervalTree.h index a7592c4..ad2d586 100644 --- a/thirdparty/intervaltree/IntervalTree.h +++ b/ehunter/thirdparty/intervaltree/IntervalTree.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace ehunter { diff --git a/thirdparty/json/json.hpp b/ehunter/thirdparty/json/json.hpp similarity index 100% rename from thirdparty/json/json.hpp rename to ehunter/thirdparty/json/json.hpp diff --git a/ehunter/tools/README.md b/ehunter/tools/README.md new file mode 100644 index 0000000..2ecfe8f --- /dev/null +++ b/ehunter/tools/README.md @@ -0,0 +1,8 @@ +## Tools + +Scripts and tooling related to ExpansionHunter development. + +All project source code formatting can be updated by running the script `updateSourceFormatting.bash`, which standardizes +all source formating using clang-format 7.0.1, according to the project's formatting rules described in the included +`.clang-format` file. + diff --git a/ehunter/tools/clang-format b/ehunter/tools/clang-format new file mode 100755 index 0000000..6c4df8f Binary files /dev/null and b/ehunter/tools/clang-format differ diff --git a/ehunter/tools/updateSourceFormatting.bash b/ehunter/tools/updateSourceFormatting.bash new file mode 100755 index 0000000..bb89d83 --- /dev/null +++ b/ehunter/tools/updateSourceFormatting.bash @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Run clang-format on all project cxx source using clang-format v7.0.1 +# + +set -o nounset + +this_dir=$(dirname $0) + +cformat=$this_dir/clang-format + +cxx_base_dir=$(cd $this_dir/..; pwd -P) + +get_cpp_files() { + for sdir in alignment app core genotyping io locus sample tests; do + find $cxx_base_dir/$sdir -type f \( -name *.cpp -or -name *.hh \) + done +} + +# remove windows line endings from source: +get_cpp_files | xargs -P8 -n1 sed $'s/\r$//' -i + +# general c++ source reformatting: +get_cpp_files | xargs -P8 -n1 $cformat -style=file -i diff --git a/example/run.sh b/example/run.sh index 61ba702..be338a3 100644 --- a/example/run.sh +++ b/example/run.sh @@ -1,6 +1,6 @@ #!/bin/bash -../build/ExpansionHunter \ +../bin/ExpansionHunter \ --reads input/variants.bam \ --reference input/reference.fa \ --variant-catalog input/variants.json \ diff --git a/filtering/CMakeLists.txt b/filtering/CMakeLists.txt deleted file mode 100644 index 7eec568..0000000 --- a/filtering/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(filtering ${SOURCES}) -target_link_libraries(filtering common graphtools) - -add_subdirectory(tests) diff --git a/filtering/tests/CMakeLists.txt b/filtering/tests/CMakeLists.txt deleted file mode 100644 index 73f2b22..0000000 --- a/filtering/tests/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_executable(OrientationPredictorTest OrientationPredictorTest.cpp) -target_link_libraries(OrientationPredictorTest filtering gtest_main) -add_test(NAME OrientationPredictorTest COMMAND OrientationPredictorTest) diff --git a/filtering/tests/OrientationPredictorTest.cpp b/filtering/tests/OrientationPredictorTest.cpp deleted file mode 100644 index 8c9ad49..0000000 --- a/filtering/tests/OrientationPredictorTest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "filtering/OrientationPredictor.hh" - -#include - -#include "gtest/gtest.h" - -#include "graphalign/GappedAligner.hh" -#include "graphalign/GraphAlignment.hh" -#include "graphalign/GraphAlignmentOperations.hh" -#include "graphcore/Graph.hh" -#include "graphcore/GraphBuilders.hh" -#include "graphcore/Path.hh" -#include "graphutils/SequenceOperations.hh" - -#include "input/RegionGraph.hh" -#include "region_spec/LocusSpecification.hh" - -using graphtools::reverseComplement; -using std::string; - -using namespace ehunter; - -TEST(PredictingQueryOrientation, TypicalQueries_Classified) -{ - graphtools::Graph graph = graphtools::makeStrGraph("TAAT", "CCG", "CCTTA"); - - OrientationPredictor orientationPredictor(&graph); - - const string read = "ATCCGCCGCCGCCGCCGCCGCCGCCGCCGCCGCCTTA"; - - EXPECT_EQ(OrientationPrediction::kAlignsInOriginalOrientation, orientationPredictor.predict(read)); - - EXPECT_EQ( - OrientationPrediction::kAlignsInReverseComplementOrientation, - orientationPredictor.predict(reverseComplement(read))); - - const string homopolymer(150, 'A'); - EXPECT_EQ(OrientationPrediction::kDoesNotAlign, orientationPredictor.predict(homopolymer)); -} diff --git a/genotyping/CMakeLists.txt b/genotyping/CMakeLists.txt deleted file mode 100644 index 3836531..0000000 --- a/genotyping/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -add_library(genotyping - AlignMatrix.hh - AlignMatrix.cpp - AlleleChecker.hh - AlleleChecker.cpp - RepeatGenotype.hh - RepeatGenotype.cpp - SmallVariantGenotype.hh - SmallVariantGenotype.cpp - SmallVariantGenotyper.hh - SmallVariantGenotyper.cpp - StrAlign.hh - StrAlign.cpp - OneAlleleStrGenotyper.hh - OneAlleleStrGenotyper.cpp - TwoAlleleStrGenotyper.hh - TwoAlleleStrGenotyper.cpp - FragLogliks.hh - FragLogliks.cpp - StrGenotyper.hh - StrGenotyper.cpp - AlignMatrixFiltering.hh - AlignMatrixFiltering.cpp) -target_link_libraries(genotyping input common) -add_subdirectory(tests) diff --git a/genotyping/tests/CMakeLists.txt b/genotyping/tests/CMakeLists.txt deleted file mode 100644 index 5e1ef61..0000000 --- a/genotyping/tests/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ - -add_executable(RepeatGenotypeTest RepeatGenotypeTest.cpp) -target_link_libraries(RepeatGenotypeTest genotyping gtest_main) -add_test(NAME RepeatGenotypeTest COMMAND RepeatGenotypeTest) - -add_executable(SmallVariantGenotyperTest SmallVariantGenotyperTest.cpp) -target_link_libraries(SmallVariantGenotyperTest genotyping gtest_main) -add_test(NAME SmallVariantGenotyperTest COMMAND SmallVariantGenotyperTest) - -add_executable(AlleleCheckerTest AlleleCheckerTest.cpp) -target_link_libraries(AlleleCheckerTest genotyping gtest_main) -add_test(NAME AlleleCheckerTest COMMAND AlleleCheckerTest) - -add_executable(StrAlignTest StrAlignTest.cpp) -target_link_libraries(StrAlignTest genotyping gtest_main) -add_test(NAME StrAlignTest COMMAND StrAlignTest) - -add_executable(StrAlignMatrixTest AlignMatrixTest.cpp) -target_link_libraries(StrAlignMatrixTest genotyping gtest gmock_main) -add_test(NAME StrAlignMatrixTest COMMAND StrAlignMatrixTest) - -add_executable(FragLogliksTest FragLogliksTest.cpp) -target_link_libraries(FragLogliksTest genotyping gtest gmock_main) -add_test(NAME FragLogliksTest COMMAND FragLogliksTest) - -add_executable(StrGenotyperTest StrGenotyperTest.cpp) -target_link_libraries(StrGenotyperTest genotyping gtest gmock_main) -add_test(NAME StrGenotyperTest COMMAND StrGenotyperTest) diff --git a/genotyping/tests/GenotypeTest.cpp b/genotyping/tests/GenotypeTest.cpp deleted file mode 100644 index 4ed22c9..0000000 --- a/genotyping/tests/GenotypeTest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko , -// Mitch Bekritsky , Richard Shaw -// Concept: Michael Eberle -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "genotyping/genotype.h" - -using namespace ehunter; - -TEST(InitializingStrAlleles, TypicalAllele_Initialized) -{ - StrAllele allele(3, ReadType::kSpanning); - EXPECT_EQ(3, allele.size()); - EXPECT_EQ(Interval(3, 3), allele.sizeRange()); - EXPECT_EQ(ReadType::kSpanning, allele.supportType()); -} - -TEST(InitializingStrAlleles, AlleleSupportedBySpanningReads_SizeRangeMustEqualToSize) -{ - StrAllele allele(3, ReadType::kSpanning); - EXPECT_NO_THROW(allele.setSizeRange(3, 3)); - EXPECT_ANY_THROW(allele.setSizeRange(4, 4)); - EXPECT_ANY_THROW(allele.setSizeRange(2, 5)); -} - -TEST(InitializingStrAlleles, AlleleSupportedByFlankingOrRepeatReads_SizeRangeMustContainSize) -{ - StrAllele allele(10, ReadType::kFlanking); - EXPECT_NO_THROW(allele.setSizeRange(5, 15)); - EXPECT_ANY_THROW(allele.setSizeRange(11, 12)); - EXPECT_ANY_THROW(allele.setSizeRange(8, 9)); -} - -// TEST(DefiningStrGenotypes, TypicalStrGenotype_Test) -//{ -// StrGenotype genotype = {{3, ReadType::kSpanning}, {50, ReadType::kRepeat}}; - -// EXPECT_EQ(3, genotype.shortAlleleSize()); -// EXPECT_EQ(Interval(3, 3), genotype.shortAlleleCi()); -// EXPECT_EQ(ReadType::kSpanning, genotype.shortAlleleSupportType()); - -// EXPECT_EQ(5, genotype.longAlleleSize()); -// EXPECT_EQ(Interval(5, 5), genotype.longAlleleCi()); -// EXPECT_EQ(ReadType::kRepeat, genotype.longAlleleSupportType()); -//} diff --git a/input/CMakeLists.txt b/input/CMakeLists.txt deleted file mode 100644 index fbfadba..0000000 --- a/input/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(input ${SOURCES}) -target_link_libraries(input common stats region_spec) -add_subdirectory(tests) \ No newline at end of file diff --git a/input/tests/CMakeLists.txt b/input/tests/CMakeLists.txt deleted file mode 100644 index e336670..0000000 --- a/input/tests/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -add_executable(LocusSpecificationTest LocusSpecificationTest.cpp) -target_link_libraries(LocusSpecificationTest input gtest gmock_main) -add_test(NAME LocusSpecificationTest COMMAND LocusSpecificationTest) - -add_executable(RegionGraphTest RegionGraphTest.cpp) -target_link_libraries(RegionGraphTest input gtest gmock_main) -add_test(NAME RegionGraphTest COMMAND RegionGraphTest) - -add_executable(GraphBlueprintTest GraphBlueprintTest.cpp) -target_link_libraries(GraphBlueprintTest input gtest gmock_main) -add_test(NAME GraphBlueprintTest COMMAND GraphBlueprintTest) - diff --git a/input/tests/LocusSpecificationTest.cpp b/input/tests/LocusSpecificationTest.cpp deleted file mode 100644 index 2e8c94c..0000000 --- a/input/tests/LocusSpecificationTest.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// Concept: Michael Eberle -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "region_spec/LocusSpecification.hh" - -#include "gtest/gtest.h" - -using std::string; -using std::vector; - -using namespace ehunter; - -/* -TEST(CreatingRegionSpecs, SingeUnitRepeatSpec_Parsed) -{ - const string locusId = "region1"; - const vector repeatIds; - RegionSpec locusSpec(locusId, repeatIds, Region("chr1:1-10"), "GCC", "AT", "GCC", "TA"); - - vector expectedComponents = { RegionComponent("LF", "AT", RegionComponentType::kFlank), - RegionComponent("Repeat0", "GCC", RegionComponentType::kRepeat), - RegionComponent("RF", "TA", RegionComponentType::kFlank) }; - - ASSERT_EQ(expectedComponents, locusSpec.components()); -} - -TEST(CreatingRegionSpecs, MultiunitFromatToSpecifySingleUnitRepeat_Parsed) -{ - const string locusId = "region1"; - const vector repeatIds; - RegionSpec locusSpec(locusId, repeatIds, Region("chr1:1-10"), "(AGG)", "AT", "AGG", "TA"); - - vector expectedComponents = { RegionComponent("LF", "AT", RegionComponentType::kFlank), - RegionComponent("Repeat0", "AGG", RegionComponentType::kRepeat), - RegionComponent("RF", "TA", RegionComponentType::kFlank) }; - - ASSERT_EQ(expectedComponents, locusSpec.components()); -} - -TEST(CreatingRegionSpecs, TypicalMultiunitRepeatSpecs_Parsed) -{ - const string locusId = "region1"; - const vector repeatIds; - - { - RegionSpec locusSpec(locusId, repeatIds, Region("chr1:1-10"), "(AGG)(CG)", "AT", "AGGCG", "TA"); - - vector expectedComponents = { RegionComponent("LF", "AT", RegionComponentType::kFlank), - RegionComponent("Repeat0", "AGG", RegionComponentType::kRepeat), - RegionComponent("Repeat1", "CG", RegionComponentType::kRepeat), - RegionComponent("RF", "TA", RegionComponentType::kFlank) }; - - EXPECT_EQ(expectedComponents, locusSpec.components()); - } - - { - RegionSpec locusSpec(locusId, repeatIds, Region("chr1:1-10"), "(AGG)ATG(CG)", "CC", "AGGATGCG", "GG"); - - vector expectedComponents = { RegionComponent("LF", "CC", RegionComponentType::kFlank), - RegionComponent("Repeat0", "AGG", RegionComponentType::kRepeat), - RegionComponent("", "ATG", RegionComponentType::kInterruption), - RegionComponent("Repeat1", "CG", RegionComponentType::kRepeat), - RegionComponent("RF", "GG", RegionComponentType::kFlank) }; - - EXPECT_EQ(expectedComponents, locusSpec.components()); - } -} -*/ diff --git a/output/CMakeLists.txt b/output/CMakeLists.txt deleted file mode 100644 index c1cef5f..0000000 --- a/output/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(output ${SOURCES}) -target_link_libraries(output common stats genotyping region_spec) diff --git a/reads/CMakeLists.txt b/reads/CMakeLists.txt deleted file mode 100644 index b0d4417..0000000 --- a/reads/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(reads ${SOURCES}) -target_link_libraries(reads graphtools common) -add_subdirectory(tests) \ No newline at end of file diff --git a/reads/tests/CMakeLists.txt b/reads/tests/CMakeLists.txt deleted file mode 100644 index 2e53de6..0000000 --- a/reads/tests/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_executable(ReadTest ReadTest.cpp) -target_link_libraries(ReadTest reads gtest_main) -add_test(NAME ReadTest COMMAND ReadTest) diff --git a/region_analysis/CMakeLists.txt b/region_analysis/CMakeLists.txt deleted file mode 100644 index 90aa749..0000000 --- a/region_analysis/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(region_analysis ${SOURCES}) -target_link_libraries(region_analysis common input reads classification region_spec genotyping alignment filtering ${htslib_static} ${zlib_static}) -add_subdirectory(tests) \ No newline at end of file diff --git a/region_analysis/LocusAnalyzer.cpp b/region_analysis/LocusAnalyzer.cpp deleted file mode 100644 index 9365039..0000000 --- a/region_analysis/LocusAnalyzer.cpp +++ /dev/null @@ -1,276 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "region_analysis/LocusAnalyzer.hh" - -#include -#include - -// clang-format off -// Note that spdlog.h must be included before ostr.h -#include "thirdparty/spdlog/include/spdlog/spdlog.h" -#include "thirdparty/spdlog/include/spdlog/fmt/ostr.h" -// clang-format on - -#include "graphalign/GraphAlignmentOperations.hh" -#include "graphutils/SequenceOperations.hh" - -#include "alignment/AlignmentFilters.hh" -#include "alignment/OperationsOnAlignments.hh" -#include "region_analysis/RepeatAnalyzer.hh" -#include "region_analysis/SmallVariantAnalyzer.hh" - -namespace ehunter -{ - -#include - -using boost::optional; -using graphtools::AlignmentWriter; -using graphtools::Operation; -using graphtools::OperationType; -using graphtools::splitStringByDelimiter; -using std::list; -using std::string; -using std::unique_ptr; -using std::vector; - -/* -static const string encodeReadPair(const Read& read, const Read& mate) -{ - std::stringstream encoding; - encoding << read.readId() << ": " << read.sequence() << "\n" << mate.readId() << ": " << read.sequence(); - return encoding.str(); -} -*/ - -LocusAnalyzer::LocusAnalyzer( - const LocusSpecification& locusSpec, HeuristicParameters heuristicParams, - graphtools::AlignmentWriter& alignmentWriter) - : locusSpec_(locusSpec) - , heuristicParams_(heuristicParams) - , alignmentWriter_(alignmentWriter) - , orientationPredictor_(&locusSpec_.regionGraph()) - , graphAligner_( - &locusSpec_.regionGraph(), heuristicParams.alignerType(), heuristicParams_.kmerLenForAlignment(), - heuristicParams_.paddingLength(), heuristicParams_.seedAffixTrimLength()) - , statsCalculator_(locusSpec_.typeOfChromLocusLocatedOn(), locusSpec_.regionGraph()) - -{ - for (const auto& variantSpec : locusSpec_.variantSpecs()) - { - if (variantSpec.classification().type == VariantType::kRepeat) - { - const auto& graph = locusSpec_.regionGraph(); - const int repeatNodeId = variantSpec.nodes().front(); - const string& repeatUnit = graph.nodeSeq(repeatNodeId); - - weightedPurityCalculators.emplace(std::make_pair(repeatUnit, WeightedPurityCalculator(repeatUnit))); - - if (variantSpec.classification().subtype == VariantSubtype::kRareRepeat) - { - if (optionalUnitOfRareRepeat_) - { - const string errorMessage - = "Region " + locusSpec_.locusId() + " is not permitted to have more than one rare variant"; - throw std::logic_error(errorMessage); - } - optionalUnitOfRareRepeat_ = repeatUnit; - } - - variantAnalyzerPtrs_.emplace_back(new RepeatAnalyzer( - variantSpec.id(), locusSpec.regionGraph(), repeatNodeId, locusSpec.genotyperParameters())); - } - else if (variantSpec.classification().type == VariantType::kSmallVariant) - { - variantAnalyzerPtrs_.emplace_back(new SmallVariantAnalyzer( - variantSpec.id(), variantSpec.classification().subtype, locusSpec.regionGraph(), variantSpec.nodes(), - variantSpec.optionalRefNode(), locusSpec.genotyperParameters())); - } - else - { - std::stringstream encoding; - encoding << variantSpec.classification().type << "/" << variantSpec.classification().subtype; - throw std::logic_error("Missing logic to create an analyzer for " + encoding.str()); - } - } -} - -void LocusAnalyzer::processMates(Read read, optional mate, RegionType regionType) -{ - if (regionType == RegionType::kTarget) - { - processOntargetMates(std::move(read), std::move(mate)); - } - else if (mate) - { - processOfftargetMates(std::move(read), std::move(*mate)); - } -} - -void LocusAnalyzer::processOntargetMates(Read read, optional mate) -{ - optional readAlignment = alignRead(read); - optional mateAlignment = mate ? alignRead(*mate) : boost::none; - - int numMatchingBases = read.sequence().length() / 7.5; - numMatchingBases = std::max(numMatchingBases, 10); - LinearAlignmentParameters parameters; - const int kMinNonRepeatAlignmentScore = numMatchingBases * parameters.matchScore; - - if (!checkIfLocallyPlacedReadPair(readAlignment, mateAlignment, kMinNonRepeatAlignmentScore)) - { - if (mate && optionalUnitOfRareRepeat_) - { - processOfftargetMates(std::move(read), std::move(*mate)); - } - - return; - } - - if (readAlignment && mateAlignment) - { - statsCalculator_.inspect(*readAlignment, *mateAlignment); - runVariantAnalysis(read, *readAlignment, *mate, *mateAlignment); - } - else - { - const string readStatus = readAlignment ? "Able" : "Unable"; - spdlog::debug("{} to align {} to {}: {}", readStatus, read.readId(), locusSpec_.locusId(), read.sequence()); - if (mate) - { - const string mateStatus = mateAlignment ? "Able" : "Unable"; - spdlog::debug( - "{} to align {} to {}: {}", mateStatus, mate->readId(), locusSpec_.locusId(), mate->sequence()); - } - } -} - -void LocusAnalyzer::processOfftargetMates(Read read, Read mate) -{ - if (!optionalUnitOfRareRepeat_) - { - const string errorMessage - = "Cannot process offtarget mates for " + locusSpec_.locusId() + " because repeat unit is not set"; - throw std::logic_error(errorMessage); - } - - const string& repeatUnit = *optionalUnitOfRareRepeat_; - - const auto& weightedPurityCalculator = weightedPurityCalculators.at(repeatUnit); - const double kPurityCutoff = 0.90; - const bool isFirstReadInrepeat = weightedPurityCalculator.score(read.sequence()) >= kPurityCutoff; - const bool isSecondReadInrepeat = weightedPurityCalculator.score(mate.sequence()) >= kPurityCutoff; - - if (isFirstReadInrepeat && isSecondReadInrepeat) - { - int numAnalyzersFound = 0; - for (auto& variantAnalyzerPtr : variantAnalyzerPtrs_) - { - auto repeatAnalyzerPtr = dynamic_cast(variantAnalyzerPtr.get()); - if (repeatAnalyzerPtr != nullptr && repeatAnalyzerPtr->repeatUnit() == repeatUnit) - { - numAnalyzersFound++; - repeatAnalyzerPtr->addInrepeatReadPair(); - } - } - - if (numAnalyzersFound != 1) - { - const string errorMessage = "Encountered inconsistently-specified locus " + locusSpec_.locusId(); - throw std::logic_error(errorMessage); - } - } -} - -void LocusAnalyzer::runVariantAnalysis( - const Read& read, const GraphAlignment& readAlignment, const Read& mate, const GraphAlignment& mateAlignment) -{ - alignmentWriter_.write( - locusSpec_.locusId(), read.fragmentId(), read.sequence(), read.isFirstMate(), read.isReversed(), - mate.isReversed(), readAlignment); - alignmentWriter_.write( - locusSpec_.locusId(), mate.fragmentId(), mate.sequence(), mate.isFirstMate(), mate.isReversed(), - read.isReversed(), mateAlignment); - - for (auto& variantAnalyzerPtr : variantAnalyzerPtrs_) - { - variantAnalyzerPtr->processMates(read, readAlignment, mate, mateAlignment); - } -} - -boost::optional LocusAnalyzer::alignRead(Read& read) const -{ - OrientationPrediction predictedOrientation = orientationPredictor_.predict(read.sequence()); - - if (predictedOrientation == OrientationPrediction::kAlignsInReverseComplementOrientation) - { - read.reverseComplement(); - } - else if (predictedOrientation == OrientationPrediction::kDoesNotAlign) - { - return boost::optional(); - } - - const list alignments = graphAligner_.align(read.sequence()); - - if (alignments.empty()) - { - return boost::optional(); - } - - return computeCanonicalAlignment(alignments); -} - -LocusFindings LocusAnalyzer::analyze(Sex sampleSex, boost::optional genomeWideDepth) -{ - LocusFindings locusFindings(statsCalculator_.estimate(sampleSex)); - if (genomeWideDepth && locusSpec_.requiresGenomeWideDepth()) - { - locusFindings.stats.setDepth(*genomeWideDepth); - } - - for (auto& variantAnalyzerPtr : variantAnalyzerPtrs_) - { - std::unique_ptr variantFindingsPtr = variantAnalyzerPtr->analyze(locusFindings.stats); - const string& variantId = variantAnalyzerPtr->variantId(); - locusFindings.findingsForEachVariant.emplace(variantId, std::move(variantFindingsPtr)); - } - - return locusFindings; -} - -vector> initializeLocusAnalyzers( - const RegionCatalog& RegionCatalog, const HeuristicParameters& heuristicParams, AlignmentWriter& bamletWriter) -{ - vector> locusAnalyzers; - - for (const auto& locusIdAndRegionSpec : RegionCatalog) - { - const LocusSpecification& locusSpec = locusIdAndRegionSpec.second; - - locusAnalyzers.emplace_back(new LocusAnalyzer(locusSpec, heuristicParams, bamletWriter)); - } - - return locusAnalyzers; -} - -} diff --git a/region_analysis/LocusAnalyzer.hh b/region_analysis/LocusAnalyzer.hh deleted file mode 100644 index da966b5..0000000 --- a/region_analysis/LocusAnalyzer.hh +++ /dev/null @@ -1,105 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#pragma once - -#include -#include -#include -#include - -#include - -#include "graphalign/GappedAligner.hh" -#include "graphalign/KmerIndex.hh" -#include "graphio/AlignmentWriter.hh" - -#include "alignment/SoftclippingAligner.hh" -#include "common/Parameters.hh" -#include "filtering/OrientationPredictor.hh" -#include "reads/Read.hh" -#include "region_analysis/LocusFindings.hh" -#include "region_analysis/VariantAnalyzer.hh" -#include "region_spec/LocusSpecification.hh" -#include "stats/LocusStats.hh" -#include "stats/WeightedPurityCalculator.hh" - -namespace ehunter -{ - -// Regions of the reference genome that can contain reads that originated in a given locus are partitioned into target -// and offtarget regions. Target regions typically consist of the reference region of the locus and possibly other -// highly-similar regions where reads typically misalign. Offtarget regions are regions where certain kinds of -// relevant reads might occasionally misalign and that require special handling (usually for efficiency reasons). -enum class RegionType -{ - kTarget, - kOfftarget -}; - -class LocusAnalyzer -{ -public: - LocusAnalyzer( - const LocusSpecification& locusSpec, HeuristicParameters heuristicParams, - graphtools::AlignmentWriter& alignmentWriter); - - LocusAnalyzer(const LocusAnalyzer&) = delete; - LocusAnalyzer& operator=(const LocusAnalyzer&) = delete; - LocusAnalyzer(LocusAnalyzer&&) = default; - - const std::string& locusId() const { return locusSpec_.locusId(); } - const LocusSpecification& locusSpec() const { return locusSpec_; } - - void processMates(Read read, boost::optional mate, RegionType regionType); - - bool checkIfPassesSequenceFilters(const std::string& sequence) const; - - LocusFindings analyze(Sex sampleSex, boost::optional genomeWideDepth); - - bool operator==(const LocusAnalyzer& other) const; - -private: - void processOntargetMates(Read read, boost::optional mate); - void processOfftargetMates(Read read, Read mate); - void runVariantAnalysis( - const Read& read, const GraphAlignment& readAlignment, const Read& mate, const GraphAlignment& mateAlignment); - boost::optional alignRead(Read& read) const; - - LocusSpecification locusSpec_; - HeuristicParameters heuristicParams_; - - graphtools::AlignmentWriter& alignmentWriter_; - OrientationPredictor orientationPredictor_; - SoftclippingAligner graphAligner_; - - std::unordered_map weightedPurityCalculators; - LocusStatsCalculator statsCalculator_; - - std::vector> variantAnalyzerPtrs_; - boost::optional optionalUnitOfRareRepeat_; -}; - -std::vector> initializeLocusAnalyzers( - const RegionCatalog& RegionCatalog, const HeuristicParameters& heuristicParams, - graphtools::AlignmentWriter& alignmentWriter); - -} diff --git a/region_analysis/tests/CMakeLists.txt b/region_analysis/tests/CMakeLists.txt deleted file mode 100644 index 7ce6aa7..0000000 --- a/region_analysis/tests/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(RepeatAnalyzerTest RepeatAnalyzerTest.cpp) -target_link_libraries(RepeatAnalyzerTest region_analysis gtest gmock_main) -add_test(NAME RepeatAnalyzerTest COMMAND RepeatAnalyzerTest) - -add_executable(LocusAnalyzerTest LocusAnalyzerTest.cpp) -target_link_libraries(LocusAnalyzerTest region_analysis gtest gmock_main) -add_test(NAME LocusAnalyzerTest COMMAND LocusAnalyzerTest) diff --git a/region_analysis/tests/LocusAnalyzerTest.cpp b/region_analysis/tests/LocusAnalyzerTest.cpp deleted file mode 100644 index 3b77a54..0000000 --- a/region_analysis/tests/LocusAnalyzerTest.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "region_analysis/LocusAnalyzer.hh" - -#include "gtest/gtest.h" - -#include "input/GraphBlueprint.hh" -#include "input/RegionGraph.hh" -#include "region_spec/LocusSpecification.hh" - -using namespace ehunter; - -using graphtools::Graph; -using std::string; -using std::vector; - -class AlignerTests : public ::testing::TestWithParam -{ -}; - -// TODO: Throw an error if there are no valid extensions? -TEST_P(AlignerTests, RegionAnalysis_ShortSingleUnitRepeat_Genotyped) -{ - Graph graph = makeRegionGraph(decodeFeaturesFromRegex("ATTCGA(C)*ATGTCG")); - vector referenceRegions = { GenomicRegion(1, 1, 2) }; - - NodeToRegionAssociation dummyAssociation; - GenotyperParameters params; - LocusSpecification locusSpec("region", ChromType::kAutosome, referenceRegions, graph, dummyAssociation, params); - VariantClassification classification(VariantType::kRepeat, VariantSubtype::kCommonRepeat); - locusSpec.addVariantSpecification("repeat", classification, GenomicRegion(1, 1, 2), { 1 }, 1); - - HeuristicParameters heuristicParams(1000, 20, true, GetParam(), 4, 1, 5); - - graphtools::BlankAlignmentWriter blankAlignmentWriter; - LocusAnalyzer locusAnalyzer(locusSpec, heuristicParams, blankAlignmentWriter); - - locusAnalyzer.processMates( - Read(ReadId("read1", MateNumber::kFirstMate), "CGACCCATGT", true), - Read(ReadId("read1", MateNumber::kSecondMate), "GACCCATGTC", true), RegionType::kTarget); - - locusAnalyzer.processMates( - Read(ReadId("read2", MateNumber::kFirstMate), "CGACATGT", true), - Read(ReadId("read2", MateNumber::kSecondMate), "GACATGTC", true), RegionType::kTarget); - - LocusFindings locusFindings = locusAnalyzer.analyze(Sex::kFemale, boost::none); - GenotypeFilter filter = GenotypeFilter(); - - std::unique_ptr repeatFindingsPtr(new RepeatFindings( - CountTable({ { 1, 2 }, { 3, 2 } }), CountTable(), CountTable(), AlleleCount::kTwo, RepeatGenotype(1, { 1, 3 }), - filter)); - LocusFindings expectedFindings((LocusStats(AlleleCount::kTwo, 0, 0, 0.0))); - expectedFindings.findingsForEachVariant.emplace("repeat", std::move(repeatFindingsPtr)); - - ASSERT_EQ(expectedFindings.findingsForEachVariant, locusFindings.findingsForEachVariant); -} - -TEST_P(AlignerTests, RegionAnalysis_ShortMultiUnitRepeat_Genotyped) -{ - // const int32_t kmerLenForReadOrientation = 5; - // const int32_t kmerLenForAlignment = 4; - // const int32_t paddingLength = 1; -} - -// INSTANTIATE_TEST_CASE_P( -// AlignerTestsInst, AlignerTests, ::testing::Values(std::string("path-aligner"), std::string("dag-aligner")), ); diff --git a/region_spec/CMakeLists.txt b/region_spec/CMakeLists.txt deleted file mode 100644 index af20014..0000000 --- a/region_spec/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(region_spec ${SOURCES}) -target_link_libraries(region_spec common graphtools) \ No newline at end of file diff --git a/sample_analysis/AnalyzerFinder.cpp b/sample_analysis/AnalyzerFinder.cpp deleted file mode 100644 index 868396b..0000000 --- a/sample_analysis/AnalyzerFinder.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko , -// Felix Schlesinger -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/AnalyzerFinder.hh" - -using boost::optional; -using std::unique_ptr; -using std::unordered_map; -using std::vector; - -namespace ehunter -{ - -namespace -{ - bool areMatesNearby(int32_t readContigId, int64_t readPosition, int32_t mateContigId, int64_t matePosition) - { - const int kMaxMateDistance = 1000; - return ((readContigId == mateContigId) && (std::abs(readPosition - matePosition) < kMaxMateDistance)); - } - - inline RegionType coalesceRegionTypes(RegionType readRegionType, RegionType mateRegionType) - { - if (readRegionType == RegionType::kTarget || mateRegionType == RegionType::kTarget) - { - return RegionType::kTarget; - } - - return RegionType::kOfftarget; - } - - vector - coalesceCommonBundles(const vector& readBundles, const vector& mateBundles) - { - vector commonBundles; - - for (const auto& readBundle : readBundles) - { - for (const auto& mateBundle : mateBundles) - { - if (readBundle.locusAnalyzerPtr == mateBundle.locusAnalyzerPtr) - { - commonBundles.push_back(readBundle); - commonBundles.back().regionType = coalesceRegionTypes(readBundle.regionType, mateBundle.regionType); - } - } - } - - return commonBundles; - } - - // We ignore nearby pairs where one mate is inside and one mate is outside of the offtarget region - vector - coalesceBundlesForNearbyMates(const vector& readBundles, const vector& mateBundles) - { - vector bundles; - - for (const auto& bundle : readBundles) - { - if (bundle.regionType == RegionType::kTarget) - { - bundles.push_back(bundle); - bundles.back().inputType = AnalyzerInputType::kReadOnly; - } - } - - for (const auto& bundle : mateBundles) - { - if (bundle.regionType == RegionType::kTarget) - { - bundles.push_back(bundle); - bundles.back().inputType = AnalyzerInputType::kMateOnly; - } - } - - return bundles; - } - - vector - coalesceBundlesForFarawayMates(const vector& readBundles, const vector& mateBundles) - { - vector bundles; - - for (const auto& bundle : readBundles) - { - bundles.push_back(bundle); - bundles.back().inputType = AnalyzerInputType::kBothReads; - } - - for (const auto& bundle : mateBundles) - { - bundles.push_back(bundle); - bundles.back().inputType = AnalyzerInputType::kBothReads; - } - - return bundles; - } -} - -AnalyzerFinder::AnalyzerFinder(vector>& locusAnalyzers) -{ - using IntervalWithLocusTypeAndAnalyzer = ehunter::Interval; - - unordered_map> contigToIntervals; - for (auto& locusAnalyzer : locusAnalyzers) - { - const LocusSpecification& locusSpec = locusAnalyzer->locusSpec(); - for (const auto& region : locusSpec.targetReadExtractionRegions()) - { - AnalyzerBundle bundle(RegionType::kTarget, locusAnalyzer.get()); - contigToIntervals[region.contigIndex()].emplace_back(region.start(), region.end(), bundle); - } - - for (const auto& region : locusSpec.offtargetReadExtractionRegions()) - { - AnalyzerBundle bundle(RegionType::kOfftarget, locusAnalyzer.get()); - contigToIntervals[region.contigIndex()].emplace_back(region.start(), region.end(), bundle); - } - } - - for (auto& contigAndIntervals : contigToIntervals) - { - int32_t contigIndex = contigAndIntervals.first; - auto intervals = contigAndIntervals.second; - - intervalTrees_.emplace(std::make_pair(contigIndex, AnalyzerIntervalTree(std::move(intervals)))); - } -} - -vector AnalyzerFinder::query(int32_t contigIndex, int64_t start, int64_t end) const -{ - const auto contigTreeIterator = intervalTrees_.find(contigIndex); - if (contigTreeIterator == intervalTrees_.end()) - { - return vector(); - } - - const auto& intervalsWithBundles = contigTreeIterator->second.findOverlapping(start, end); - - vector analyzerBundles; - for (const auto& intervalWithBundle : intervalsWithBundles) - { - const bool isReadContainedInInterval = static_cast(intervalWithBundle.start) <= start - && end <= static_cast(intervalWithBundle.stop); - if (isReadContainedInInterval) - { - analyzerBundles.push_back(intervalWithBundle.value); - } - } - - return analyzerBundles; -} - -vector AnalyzerFinder::query( - int32_t readContigId, int64_t readStart, int64_t readEnd, int32_t mateContigId, int64_t mateStart, - int64_t mateEnd) const -{ - vector readAnalyzerBundles = query(readContigId, readStart, readEnd); - vector mateAnalyzerBundles = query(mateContigId, mateStart, mateEnd); - vector commonBundles = coalesceCommonBundles(readAnalyzerBundles, mateAnalyzerBundles); - - if (!commonBundles.empty()) - { - return commonBundles; - } - else if (areMatesNearby(readContigId, readStart, mateContigId, mateStart)) - { - return coalesceBundlesForNearbyMates(readAnalyzerBundles, mateAnalyzerBundles); - } - else - { - return coalesceBundlesForFarawayMates(readAnalyzerBundles, mateAnalyzerBundles); - } -} - -} diff --git a/sample_analysis/CMakeLists.txt b/sample_analysis/CMakeLists.txt deleted file mode 100644 index df3ff3d..0000000 --- a/sample_analysis/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(sample_analysis ${SOURCES}) -target_link_libraries(sample_analysis region_analysis common) -add_subdirectory(tests) diff --git a/sample_analysis/HtsFileSeeker.cpp b/sample_analysis/HtsFileSeeker.cpp deleted file mode 100644 index ba4d7ab..0000000 --- a/sample_analysis/HtsFileSeeker.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/HtsFileSeeker.hh" - -#include -#include - -#include "common/HtsHelpers.hh" - -using std::string; - -namespace ehunter -{ - -namespace htshelpers -{ - - HtsFileSeeker::HtsFileSeeker(const string& htsFilePath, const std::string& htsReferencePath) - : htsFilePath_(htsFilePath) - , htsReferencePath_(htsReferencePath) - , contigInfo_({}) - { - openFile(); - loadHeader(); - loadIndex(); - htsAlignmentPtr_ = bam_init1(); - } - - HtsFileSeeker::~HtsFileSeeker() - { - bam_destroy1(htsAlignmentPtr_); - htsAlignmentPtr_ = nullptr; - - if (htsRegionPtr_) - { - hts_itr_destroy(htsRegionPtr_); - htsRegionPtr_ = nullptr; - } - - hts_idx_destroy(htsIndexPtr_); - htsIndexPtr_ = nullptr; - - bam_hdr_destroy(htsHeaderPtr_); - htsHeaderPtr_ = nullptr; - - sam_close(htsFilePtr_); - htsFilePtr_ = nullptr; - } - - void HtsFileSeeker::openFile() - { - htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); - - if (!htsFilePtr_) - { - throw std::runtime_error("Failed to read BAM file " + htsFilePath_); - } - - // Required step for parsing of some CRAMs - if (hts_set_fai_filename(htsFilePtr_, htsReferencePath_.c_str()) != 0) - { - throw std::runtime_error("Failed to set index of: " + htsReferencePath_); - } - } - - void HtsFileSeeker::loadHeader() - { - htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); - - if (!htsHeaderPtr_) - { - throw std::runtime_error("Failed to read header of " + htsFilePath_); - } - - contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); - } - - void HtsFileSeeker::loadIndex() - { - htsIndexPtr_ = sam_index_load(htsFilePtr_, htsFilePath_.c_str()); - - if (!htsIndexPtr_) - { - throw std::runtime_error("Failed to read index of " + htsFilePath_); - } - } - - void HtsFileSeeker::closeRegion() - { - if (htsRegionPtr_) - { - hts_itr_destroy(htsRegionPtr_); - htsRegionPtr_ = nullptr; - } - } - - void HtsFileSeeker::setRegion(const GenomicRegion& region) - { - closeRegion(); - - htsRegionPtr_ = sam_itr_queryi(htsIndexPtr_, region.contigIndex(), region.start(), region.end()); - - if (htsRegionPtr_ == nullptr) - { - throw std::runtime_error("Failed to extract reads from " + encode(contigInfo_, region)); - } - - status_ = Status::kStreamingReads; - } - - bool HtsFileSeeker::trySeekingToNextPrimaryAlignment() - { - if (status_ != Status::kStreamingReads) - { - return false; - } - - int32_t returnCode = 0; - - while ((returnCode = sam_itr_next(htsFilePtr_, htsRegionPtr_, htsAlignmentPtr_)) >= 0) - { - if (isPrimaryAlignment(htsAlignmentPtr_)) - return true; - } - - status_ = Status::kFinishedStreaming; - - if (returnCode < -1) - { - throw std::runtime_error("Failed to extract a record from " + htsFilePath_); - } - - return false; - } - - Read HtsFileSeeker::decodeRead(LinearAlignmentStats& alignmentStats) const - { - alignmentStats = decodeAlignmentStats(htsAlignmentPtr_); - return htshelpers::decodeRead(htsAlignmentPtr_); - } - -} - -} diff --git a/sample_analysis/HtsFileSeeker.hh b/sample_analysis/HtsFileSeeker.hh deleted file mode 100644 index 4dcb983..0000000 --- a/sample_analysis/HtsFileSeeker.hh +++ /dev/null @@ -1,86 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#pragma once - -#include -#include - -extern "C" -{ -#include "htslib/hts.h" -#include "htslib/sam.h" -} - -#include "common/GenomicRegion.hh" -#include "common/ReferenceContigInfo.hh" -#include "reads/Read.hh" - -namespace ehunter -{ - -namespace htshelpers -{ - - class HtsFileSeeker - { - public: - HtsFileSeeker(const std::string& htsFilePath, const std::string& htsReferencePath); - ~HtsFileSeeker(); - void setRegion(const GenomicRegion& region); - bool trySeekingToNextPrimaryAlignment(); - - int32_t currentReadChromIndex() const; - const std::string& currentReadChrom() const; - int32_t currentReadPosition() const; - int32_t currentMateChromIndex() const; - const std::string& currentMateChrom() const; - int32_t currentMatePosition() const; - - Read decodeRead(LinearAlignmentStats& alignmentStats) const; - - private: - enum class Status - { - kStreamingReads, - kFinishedStreaming - }; - - void openFile(); - void loadHeader(); - void loadIndex(); - void closeRegion(); - - const std::string htsFilePath_; - const std::string htsReferencePath_; - ReferenceContigInfo contigInfo_; - Status status_ = Status::kFinishedStreaming; - - htsFile* htsFilePtr_ = nullptr; - bam_hdr_t* htsHeaderPtr_ = nullptr; - hts_idx_t* htsIndexPtr_ = nullptr; - hts_itr_t* htsRegionPtr_ = nullptr; - bam1_t* htsAlignmentPtr_ = nullptr; - }; - -} - -} diff --git a/sample_analysis/HtsFileStreamer.cpp b/sample_analysis/HtsFileStreamer.cpp deleted file mode 100644 index 00e1190..0000000 --- a/sample_analysis/HtsFileStreamer.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/HtsFileStreamer.hh" - -#include "common/HtsHelpers.hh" - -using std::string; - -namespace ehunter -{ - -namespace htshelpers -{ - - void HtsFileStreamer::openHtsFile() - { - htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); - - if (!htsFilePtr_) - { - throw std::runtime_error("Failed to read BAM file " + htsFilePath_); - } - } - - void HtsFileStreamer::loadHeader() - { - htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); - - if (!htsHeaderPtr_) - { - throw std::runtime_error("Failed to read header of " + htsFilePath_); - } - - contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); - } - - void HtsFileStreamer::prepareForStreamingAlignments() { htsAlignmentPtr_ = bam_init1(); } - - bool HtsFileStreamer::trySeekingToNextPrimaryAlignment() - { - if (status_ != Status::kStreamingReads) - { - return false; - } - - int32_t returnCode = 0; - - while ((returnCode = sam_read1(htsFilePtr_, htsHeaderPtr_, htsAlignmentPtr_)) >= 0) - { - if (isPrimaryAlignment(htsAlignmentPtr_)) - return true; - } - - status_ = Status::kFinishedStreaming; - - if (returnCode < -1) - { - throw std::runtime_error("Failed to extract a record from " + htsFilePath_); - } - - return false; - } - - int32_t HtsFileStreamer::currentReadContigId() const { return htsAlignmentPtr_->core.tid; } - int32_t HtsFileStreamer::currentReadPosition() const { return htsAlignmentPtr_->core.pos; } - int32_t HtsFileStreamer::currentMateContigId() const { return htsAlignmentPtr_->core.mtid; } - int32_t HtsFileStreamer::currentMatePosition() const { return htsAlignmentPtr_->core.mpos; } - - bool HtsFileStreamer::isStreamingAlignedReads() const - { - return status_ != Status::kFinishedStreaming && currentReadContigId() != -1; - } - - Read HtsFileStreamer::decodeRead(LinearAlignmentStats& alignmentStats) const - { - alignmentStats = decodeAlignmentStats(htsAlignmentPtr_); - return htshelpers::decodeRead(htsAlignmentPtr_); - } - - HtsFileStreamer::~HtsFileStreamer() - { - bam_destroy1(htsAlignmentPtr_); - htsAlignmentPtr_ = nullptr; - - bam_hdr_destroy(htsHeaderPtr_); - htsHeaderPtr_ = nullptr; - - sam_close(htsFilePtr_); - htsFilePtr_ = nullptr; - } - -} - -} diff --git a/sample_analysis/HtsFileStreamer.hh b/sample_analysis/HtsFileStreamer.hh deleted file mode 100644 index d794af3..0000000 --- a/sample_analysis/HtsFileStreamer.hh +++ /dev/null @@ -1,88 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#pragma once - -#include -#include - -extern "C" -{ -#include "htslib/hts.h" -#include "htslib/sam.h" -} - -#include "common/ReferenceContigInfo.hh" -#include "reads/Read.hh" - -namespace ehunter -{ - -namespace htshelpers -{ - - class HtsFileStreamer - { - public: - HtsFileStreamer(const std::string& htsFilePath) - : htsFilePath_(htsFilePath) - , contigInfo_({}) - { - openHtsFile(); - loadHeader(); - prepareForStreamingAlignments(); - } - ~HtsFileStreamer(); - - bool trySeekingToNextPrimaryAlignment(); - - int32_t currentReadContigId() const; - int32_t currentReadPosition() const; - int32_t currentReadLength() const; - int32_t currentMateContigId() const; - int32_t currentMatePosition() const; - - bool isStreamingAlignedReads() const; - - Read decodeRead(LinearAlignmentStats& alignmentStats) const; - - private: - enum class Status - { - kStreamingReads, - kFinishedStreaming - }; - - void openHtsFile(); - void loadHeader(); - void prepareForStreamingAlignments(); - - const std::string htsFilePath_; - ReferenceContigInfo contigInfo_; - Status status_ = Status::kStreamingReads; - - htsFile* htsFilePtr_ = nullptr; - bam1_t* htsAlignmentPtr_ = nullptr; - bam_hdr_t* htsHeaderPtr_ = nullptr; - }; - -} -} diff --git a/sample_analysis/HtsSeekingSampleAnalysis.cpp b/sample_analysis/HtsSeekingSampleAnalysis.cpp deleted file mode 100644 index 59c7016..0000000 --- a/sample_analysis/HtsSeekingSampleAnalysis.cpp +++ /dev/null @@ -1,276 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/HtsSeekingSampleAnalysis.hh" - -#include -#include -#include -#include -#include - -#include -#include - -// clang-format off -// Note that spdlog.h must be included before ostr.h -#include "thirdparty/spdlog/include/spdlog/spdlog.h" -#include "thirdparty/spdlog/include/spdlog/fmt/ostr.h" -// clang-format on - - -#include "reads/ReadPairs.hh" -#include "region_analysis/LocusAnalyzer.hh" -#include "sample_analysis/AnalyzerFinder.hh" -#include "sample_analysis/HtsFileSeeker.hh" -#include "sample_analysis/IndexBasedDepthEstimate.hh" -#include "sample_analysis/MateExtractor.hh" - -namespace ehunter -{ - -using boost::optional; -using graphtools::AlignmentWriter; -using htshelpers::HtsFileSeeker; -using std::ostream; -using std::string; -using std::unique_ptr; -using std::unordered_map; -using std::vector; - -namespace -{ - using AlignmentStatsCatalog = unordered_map>; - - vector - combineRegions(const vector& targetRegions, const vector& offtargetRegions) - { - vector combinedRegions(targetRegions); - combinedRegions.insert(combinedRegions.end(), offtargetRegions.begin(), offtargetRegions.end()); - return combinedRegions; - } - - bool checkIfMatesWereMappedNearby(const LinearAlignmentStats& alignmentStats) - { - const int kMaxMateDistance = 1000; - if ((alignmentStats.chromId == alignmentStats.mateChromId) - && (std::abs(alignmentStats.pos - alignmentStats.matePos) < kMaxMateDistance)) - { - return true; - } - return false; - } - - void recoverMates( - const string& htsFilePath, const string& htsReferencePath, AlignmentStatsCatalog& alignmentStatsCatalog, - ReadPairs& readPairs) - { - htshelpers::MateExtractor mateExtractor(htsFilePath, htsReferencePath); - - for (auto& fragmentIdAndReadPair : readPairs) - { - ReadPair& readPair = fragmentIdAndReadPair.second; - - if (readPair.numMatesSet() == 2) - { - continue; - } - - const Read& read = readPair.firstMate ? *readPair.firstMate : *readPair.secondMate; - - const auto alignmentStatsIterator = alignmentStatsCatalog.find(read.readId()); - if (alignmentStatsIterator == alignmentStatsCatalog.end()) - { - throw std::logic_error("Cannot recover mate of uncatalogued read"); - } - const LinearAlignmentStats& alignmentStats = alignmentStatsIterator->second; - - if (!checkIfMatesWereMappedNearby(alignmentStats)) - { - LinearAlignmentStats mateStats; - optional optionalMate = mateExtractor.extractMate(read, alignmentStats, mateStats); - if (optionalMate) - { - const Read& mate = *optionalMate; - alignmentStatsCatalog.emplace(std::make_pair(mate.readId(), alignmentStats)); - readPairs.AddMateToExistingRead(mate); - } - else - { - spdlog::warn("Could not recover the mate of {}", read.readId()); - } - } - } - } - - ReadPairs collectCandidateReads( - const vector& targetRegions, const vector& offtargetRegions, - AlignmentStatsCatalog& alignmentStatsCatalog, const string& htsFilePath, const string& htsReferencePath) - { - vector regionsWithReads = combineRegions(targetRegions, offtargetRegions); - HtsFileSeeker htsFileSeeker(htsFilePath, htsReferencePath); - ReadPairs readPairs; - - for (const auto& regionWithReads : regionsWithReads) - { - const int numReadsBeforeCollection = readPairs.NumReads(); - htsFileSeeker.setRegion(regionWithReads); - while (htsFileSeeker.trySeekingToNextPrimaryAlignment()) - { - LinearAlignmentStats alignmentStats; - Read read = htsFileSeeker.decodeRead(alignmentStats); - if (alignmentStats.isPaired) - { - alignmentStatsCatalog.emplace(std::make_pair(read.readId(), alignmentStats)); - readPairs.Add(std::move(read)); - } - else - { - spdlog::warn("Skipping {} because it is unpaired", read.readId()); - } - } - const int numReadsCollected = readPairs.NumReads() - numReadsBeforeCollection; - spdlog::debug("Collected {} reads from {}", numReadsCollected, regionWithReads); - } - - const int numReadsBeforeRecovery = readPairs.NumReads(); - recoverMates(htsFilePath, htsReferencePath, alignmentStatsCatalog, readPairs); - const int numReadsAfterRecovery = readPairs.NumReads() - numReadsBeforeRecovery; - spdlog::debug("Recovered {} reads", numReadsAfterRecovery); - - return readPairs; - } - - void analyzeReadPair( - AnalyzerFinder& analyzerFinder, const Read& read, const Read& mate, const AlignmentStatsCatalog& alignmentStats) - { - const auto readStatsIter = alignmentStats.find(read.readId()); - const auto mateStatsIter = alignmentStats.find(mate.readId()); - - if (readStatsIter == alignmentStats.end() || mateStatsIter == alignmentStats.end()) - { - throw std::logic_error("Could not to find alignment stats for " + read.fragmentId()); - } - - const LinearAlignmentStats& readStats = readStatsIter->second; - const LinearAlignmentStats& mateStats = mateStatsIter->second; - - const int64_t readEnd = readStats.pos + read.sequence().length(); - const int64_t mateEnd = mateStats.pos + mate.sequence().length(); - vector analyzers = analyzerFinder.query( - readStats.chromId, readStats.pos, readEnd, mateStats.chromId, mateStats.pos, mateEnd); - - if (analyzers.empty()) - { - return; - } - - assert(analyzers.size() == 1); - const AnalyzerBundle& bundle = analyzers.front(); - - if (bundle.inputType == AnalyzerInputType::kBothReads) - { - bundle.locusAnalyzerPtr->processMates(read, mate, bundle.regionType); - } - else if (bundle.inputType == AnalyzerInputType::kReadOnly) - { - bundle.locusAnalyzerPtr->processMates(read, boost::none, bundle.regionType); - } - else if (bundle.inputType == AnalyzerInputType::kMateOnly) - { - bundle.locusAnalyzerPtr->processMates(mate, boost::none, bundle.regionType); - } - } - - void analyzeRead(AnalyzerFinder& analyzerFinder, const Read& read, const AlignmentStatsCatalog& alignmentStats) - { - const auto readStatsIter = alignmentStats.find(read.readId()); - - if (readStatsIter == alignmentStats.end()) - { - throw std::logic_error("Could not to find alignment stats for " + read.fragmentId()); - } - - const LinearAlignmentStats& readStats = readStatsIter->second; - const int64_t readEnd = readStats.pos + read.sequence().length(); - - vector analyzers = analyzerFinder.query(readStats.chromId, readStats.pos, readEnd); - - if (analyzers.empty()) - { - return; - } - - assert(analyzers.size() == 1); - const AnalyzerBundle& bundle = analyzers.front(); - bundle.locusAnalyzerPtr->processMates(read, boost::none, bundle.regionType); - } - - void processReads( - const ReadPairs& candidateReadPairs, const AlignmentStatsCatalog& alignmentStats, - AnalyzerFinder& analyzerFinder) - { - for (const auto& fragmentIdAndReads : candidateReadPairs) - { - const auto& readPair = fragmentIdAndReads.second; - if (readPair.numMatesSet() == 2) - { - analyzeReadPair(analyzerFinder, *readPair.firstMate, *readPair.secondMate, alignmentStats); - } - else - { - const Read& read = readPair.firstMate ? *readPair.firstMate : *readPair.secondMate; - analyzeRead(analyzerFinder, read, alignmentStats); - } - } - } -} - -SampleFindings htsSeekingSampleAnalysis( - const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, - const RegionCatalog& regionCatalog, AlignmentWriter& alignmentWriter) -{ - SampleFindings sampleFindings; - for (const auto& locusIdAndRegionSpec : regionCatalog) - { - const string& locusId = locusIdAndRegionSpec.first; - spdlog::info("Analyzing {}", locusId); - const LocusSpecification& locusSpec = locusIdAndRegionSpec.second; - - vector> locusAnalyzers; - locusAnalyzers.emplace_back(new LocusAnalyzer(locusSpec, heuristicParams, alignmentWriter)); - AnalyzerFinder analyzerFinder(locusAnalyzers); - - AlignmentStatsCatalog alignmentStats; - ReadPairs readPairs = collectCandidateReads( - locusSpec.targetReadExtractionRegions(), locusSpec.offtargetReadExtractionRegions(), alignmentStats, - inputPaths.htsFile(), inputPaths.reference()); - - processReads(readPairs, alignmentStats, analyzerFinder); - - auto variantFindings = locusAnalyzers.front()->analyze(sampleSex, boost::none); - sampleFindings.emplace(locusId, std::move(variantFindings)); - } - - return sampleFindings; -} - -} diff --git a/sample_analysis/HtsStreamingSampleAnalysis.cpp b/sample_analysis/HtsStreamingSampleAnalysis.cpp deleted file mode 100644 index 45c1bea..0000000 --- a/sample_analysis/HtsStreamingSampleAnalysis.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/HtsStreamingSampleAnalysis.hh" - -#include -#include - -#include - -#include "common/HtsHelpers.hh" -#include "region_analysis/LocusAnalyzer.hh" -#include "sample_analysis/GenomeQueryCollection.hh" -#include "sample_analysis/HtsFileStreamer.hh" - -using graphtools::AlignmentWriter; -using std::map; -using std::string; -using std::unordered_map; -using std::vector; - -namespace ehunter -{ - -static void transferReads(AnalyzerBundle& analyzerBundle, Read read, Read mate) -{ - const auto analyzerPtr = analyzerBundle.locusAnalyzerPtr; - - switch (analyzerBundle.inputType) - { - case AnalyzerInputType::kBothReads: - analyzerPtr->processMates(std::move(read), std::move(mate), analyzerBundle.regionType); - break; - case AnalyzerInputType::kReadOnly: - analyzerPtr->processMates(std::move(read), boost::none, analyzerBundle.regionType); - break; - case AnalyzerInputType::kMateOnly: - analyzerPtr->processMates(std::move(mate), boost::none, analyzerBundle.regionType); - break; - } -} - -SampleFindings htsStreamingSampleAnalysis( - const InputPaths& inputPaths, Sex sampleSex, const HeuristicParameters& heuristicParams, - const RegionCatalog& regionCatalog, AlignmentWriter& bamletWriter) -{ - vector> locusAnalyzers - = initializeLocusAnalyzers(regionCatalog, heuristicParams, bamletWriter); - GenomeQueryCollection genomeQuery(locusAnalyzers); - - using ReadCatalog = std::unordered_map; - ReadCatalog unpairedReads; - - htshelpers::HtsFileStreamer readStreamer(inputPaths.htsFile()); - while (readStreamer.trySeekingToNextPrimaryAlignment() && readStreamer.isStreamingAlignedReads()) - { - const bool isReadNearTargetRegion = genomeQuery.targetRegionMask.query( - readStreamer.currentReadContigId(), readStreamer.currentReadPosition()); - const bool isMateNearTargetRegion = genomeQuery.targetRegionMask.query( - readStreamer.currentMateContigId(), readStreamer.currentMatePosition()); - if (!isReadNearTargetRegion && !isMateNearTargetRegion) - { - continue; - } - - LinearAlignmentStats alignmentStats; - Read read = readStreamer.decodeRead(alignmentStats); - if (!alignmentStats.isPaired) - { - continue; - } - - const auto mateIterator = unpairedReads.find(read.fragmentId()); - if (mateIterator == unpairedReads.end()) - { - unpairedReads.emplace(std::make_pair(read.fragmentId(), std::move(read))); - continue; - } - Read mate = std::move(mateIterator->second); - unpairedReads.erase(mateIterator); - - const int64_t readEnd = readStreamer.currentReadPosition() + read.sequence().length(); - const int64_t mateEnd = readStreamer.currentMatePosition() + mate.sequence().length(); - - vector analyzerBundles = genomeQuery.analyzerFinder.query( - readStreamer.currentReadContigId(), readStreamer.currentReadPosition(), readEnd, - readStreamer.currentMateContigId(), readStreamer.currentMatePosition(), mateEnd); - - if (analyzerBundles.size() == 1) - { - transferReads(analyzerBundles.front(), std::move(read), std::move(mate)); - } - else - { - for (auto& analyzerBundle : analyzerBundles) - { - transferReads(analyzerBundle, read, mate); - } - } - } - - SampleFindings sampleFindings; - for (auto& locusAnalyzer : locusAnalyzers) - { - auto locusFindings = locusAnalyzer->analyze(sampleSex, boost::none); - sampleFindings.emplace(std::make_pair(locusAnalyzer->locusId(), std::move(locusFindings))); - } - - return sampleFindings; -} - -} diff --git a/sample_analysis/MateExtractor.cpp b/sample_analysis/MateExtractor.cpp deleted file mode 100644 index 0917e15..0000000 --- a/sample_analysis/MateExtractor.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// -// Expansion Hunter -// Copyright 2016-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "sample_analysis/MateExtractor.hh" - -#include - -#include "common/HtsHelpers.hh" - -namespace ehunter -{ - -using boost::optional; -using std::string; - -namespace htshelpers -{ - MateExtractor::MateExtractor(const string& htsFilePath, const std::string& htsReferencePath) - : htsFilePath_(htsFilePath) - , htsReferencePath_(htsReferencePath) - , contigInfo_({}) - { - openFile(); - loadHeader(); - loadIndex(); - htsAlignmentPtr_ = bam_init1(); - } - - MateExtractor::~MateExtractor() - { - bam_destroy1(htsAlignmentPtr_); - htsAlignmentPtr_ = nullptr; - - hts_idx_destroy(htsIndexPtr_); - htsIndexPtr_ = nullptr; - - bam_hdr_destroy(htsHeaderPtr_); - htsHeaderPtr_ = nullptr; - - sam_close(htsFilePtr_); - htsFilePtr_ = nullptr; - } - - void MateExtractor::openFile() - { - htsFilePtr_ = sam_open(htsFilePath_.c_str(), "r"); - - if (!htsFilePtr_) - { - throw std::runtime_error("Failed to read BAM file " + htsFilePath_); - } - - // Required step for parsing of some CRAMs - if (hts_set_fai_filename(htsFilePtr_, htsReferencePath_.c_str()) != 0) - { - throw std::runtime_error("Failed to set index of: " + htsReferencePath_); - } - } - - void MateExtractor::loadHeader() - { - htsHeaderPtr_ = sam_hdr_read(htsFilePtr_); - - if (!htsHeaderPtr_) - { - throw std::runtime_error("Failed to read header of " + htsFilePath_); - } - - contigInfo_ = htshelpers::decodeContigInfo(htsHeaderPtr_); - } - - void MateExtractor::loadIndex() - { - htsIndexPtr_ = sam_index_load(htsFilePtr_, htsFilePath_.c_str()); - - if (!htsIndexPtr_) - { - throw std::runtime_error("Failed to read index of " + htsFilePath_); - } - } - - optional MateExtractor::extractMate( - const Read& read, const LinearAlignmentStats& alignmentStats, LinearAlignmentStats& mateStats) - { - const int32_t searchRegionContigIndex - = alignmentStats.isMateMapped ? alignmentStats.mateChromId : alignmentStats.chromId; - - const int32_t searchRegionStart = alignmentStats.isMateMapped ? alignmentStats.matePos : alignmentStats.pos; - const int32_t searchRegionEnd = searchRegionStart + 1; - - hts_itr_t* htsRegionPtr_ - = sam_itr_queryi(htsIndexPtr_, searchRegionContigIndex, searchRegionStart, searchRegionEnd); - - if (!htsRegionPtr_) - { - const string& contigName = contigInfo_.getContigName(searchRegionContigIndex); - const string regionEncoding - = contigName + ":" + std::to_string(searchRegionStart) + "-" + std::to_string(searchRegionEnd); - - throw std::logic_error("Unable to jump to " + regionEncoding + " to recover a mate"); - } - - while (sam_itr_next(htsFilePtr_, htsRegionPtr_, htsAlignmentPtr_) >= 0) - { - const bool isSecondaryAlignment = htsAlignmentPtr_->core.flag & BAM_FSECONDARY; - const bool isSupplementaryAlignment = htsAlignmentPtr_->core.flag & BAM_FSUPPLEMENTARY; - const bool isPrimaryAlignment = !(isSecondaryAlignment || isSupplementaryAlignment); - if (!isPrimaryAlignment) - { - continue; - } - - Read putativeMate = htshelpers::decodeRead(htsAlignmentPtr_); - - const bool belongToSameFragment = read.fragmentId() == putativeMate.fragmentId(); - const bool formProperPair = read.mateNumber() != putativeMate.mateNumber(); - - if (belongToSameFragment && formProperPair) - { - mateStats = decodeAlignmentStats(htsAlignmentPtr_); - hts_itr_destroy(htsRegionPtr_); - return putativeMate; - } - } - hts_itr_destroy(htsRegionPtr_); - - return optional(); - } - -} - -} diff --git a/sample_analysis/tests/CMakeLists.txt b/sample_analysis/tests/CMakeLists.txt deleted file mode 100644 index fb7050b..0000000 --- a/sample_analysis/tests/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_executable(GenomeMaskTest GenomeMaskTest.cpp) -target_link_libraries(GenomeMaskTest sample_analysis gtest gmock_main) -add_test(NAME GenomeMaskTest COMMAND GenomeMaskTest) diff --git a/stats/CMakeLists.txt b/stats/CMakeLists.txt deleted file mode 100644 index a9ce16c..0000000 --- a/stats/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB SOURCES "*.cpp") -add_library(stats ${SOURCES}) -target_link_libraries(stats common graphtools) -add_subdirectory(tests) diff --git a/stats/tests/CMakeLists.txt b/stats/tests/CMakeLists.txt deleted file mode 100644 index 631e963..0000000 --- a/stats/tests/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_executable(ReadSupportCalculatorTest ReadSupportCalculatorTest.cpp) -target_link_libraries(ReadSupportCalculatorTest stats gtest_main) -add_test(NAME ReadSupportCalculatorTest COMMAND ReadSupportCalculatorTest) - -add_executable(WeightedPurityCalculatorTest WeightedPurityCalculatorTest.cpp) -target_link_libraries(WeightedPurityCalculatorTest stats gtest gmock_main) -add_test(NAME WeightedPurityCalculatorTest COMMAND WeightedPurityCalculatorTest) - -add_executable(LocusStatsTest LocusStatsTest.cpp) -target_link_libraries(LocusStatsTest stats gtest gmock_main) -add_test(NAME LocusStatsTest COMMAND LocusStatsTest) diff --git a/thirdparty/graph-tools-master/.clang-format b/thirdparty/graph-tools-master/.clang-format deleted file mode 100755 index 6c433b4..0000000 --- a/thirdparty/graph-tools-master/.clang-format +++ /dev/null @@ -1,6 +0,0 @@ -BasedOnStyle: WebKit -ColumnLimit: 120 -AlignAfterOpenBracket: AlwaysBreak -BreakBeforeBraces: Allman -BreakStringLiterals: 'true' -ReflowComments: 'true' diff --git a/thirdparty/graph-tools-master/external/googletest-release-1.8.0.tar.gz b/thirdparty/graph-tools-master/external/googletest-release-1.8.0.tar.gz deleted file mode 100755 index a40df33..0000000 Binary files a/thirdparty/graph-tools-master/external/googletest-release-1.8.0.tar.gz and /dev/null differ diff --git a/thirdparty/graph-tools-master/src/graphalign/KmerIndex.cpp b/thirdparty/graph-tools-master/src/graphalign/KmerIndex.cpp deleted file mode 100755 index 5165084..0000000 --- a/thirdparty/graph-tools-master/src/graphalign/KmerIndex.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// -// GraphTools library -// Copyright 2017-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko , -// Peter Krusche -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "graphalign/KmerIndex.hh" - -#include -#include -#include -#include -#include -#include - -#include "graphcore/PathOperations.hh" -#include "graphutils/PairHashing.hh" -#include "graphutils/SequenceOperations.hh" - -#include - -using std::list; -using std::string; -using std::unordered_set; -using std::vector; - -namespace graphtools -{ - -struct KmerIndex::Impl -{ - explicit Impl(StringToPathsMap kmer_to_paths_map_); - explicit Impl(const Graph& graph, size_t kmer_len_); - void addKmerPathsStartingAtNode(const Graph& graph, NodeId node_id); - void addKmerPaths(const std::list& kmer_paths); - void updateKmerCounts(); - size_t kmer_len; - StringToPathsMap kmer_to_paths_map; - std::unordered_map node_kmer_counts; - std::unordered_map edge_kmer_counts; -}; - -KmerIndex::Impl::Impl(StringToPathsMap kmer_to_paths_map_) - : kmer_to_paths_map(std::move(kmer_to_paths_map_)) -{ - kmer_len = 0; - for (const auto& kv : kmer_to_paths_map) - { - const string& kmer = kv.first; - kmer_len = kmer.length(); - break; - } - updateKmerCounts(); -} - -KmerIndex::Impl::Impl(const Graph& graph, size_t kmer_len_) - : kmer_len(kmer_len_) -{ - for (NodeId node_id = 0; node_id != graph.numNodes(); ++node_id) - { - addKmerPathsStartingAtNode(graph, node_id); - } - updateKmerCounts(); -} - -void KmerIndex::Impl::addKmerPathsStartingAtNode(const Graph& graph, NodeId node_id) -{ - const string node_seq = graph.nodeSeq(node_id); - vector node_list; - node_list.push_back(node_id); - for (size_t pos = 0; pos != node_seq.length(); ++pos) - { - Path path(&graph, static_cast(pos), node_list, static_cast(pos)); - addKmerPaths(extendPath(path, 0, static_cast(kmer_len))); - } -} - -void KmerIndex::Impl::addKmerPaths(const list& kmer_paths) -{ - for (const Path& kmer_path : kmer_paths) - { - vector expanded_sequences; - if (kmer_path.graphRawPtr()->isSequenceExpansionRequired()) - { - expandReferenceSequence(kmer_path.seq(), expanded_sequences); - } - else - { - expanded_sequences = { kmer_path.seq() }; - } - for (const auto& expanded_kmer_seq : expanded_sequences) - { - kmer_to_paths_map[expanded_kmer_seq].push_back(kmer_path); - } - } -} - -void KmerIndex::Impl::updateKmerCounts() -{ - node_kmer_counts.clear(); - edge_kmer_counts.clear(); - for (const auto& kmer_and_paths : kmer_to_paths_map) - { - // kmer is unique - if (kmer_and_paths.second.size() == 1) - { - bool has_previous = false; - NodeId previous_node = 0; - for (auto const& path_node_id : kmer_and_paths.second.front().nodeIds()) - { - node_kmer_counts[path_node_id] += 1; - if (has_previous) - { - edge_kmer_counts[std::make_pair(previous_node, path_node_id)] += 1; - } - has_previous = true; - previous_node = path_node_id; - } - } - } -} - -KmerIndex::KmerIndex(const StringToPathsMap& kmer_to_paths_map) - : pimpl_(new Impl(kmer_to_paths_map)) -{ -} - -KmerIndex::KmerIndex(const Graph& graph, int32_t kmer_len) - : pimpl_(new Impl(graph, static_cast(kmer_len))) -{ -} - -KmerIndex::KmerIndex(const KmerIndex& other) - : pimpl_(new Impl(*other.pimpl_)) -{ -} - -KmerIndex::KmerIndex(KmerIndex&& other) noexcept - : pimpl_(std::move(other.pimpl_)) -{ -} - -KmerIndex& KmerIndex::operator=(const KmerIndex& other) -{ - if (this != &other) - { - pimpl_.reset(new Impl(*other.pimpl_)); - } - return *this; -} - -KmerIndex& KmerIndex::operator=(KmerIndex&& other) noexcept -{ - pimpl_ = std::move(other.pimpl_); - return *this; -} - -KmerIndex::~KmerIndex() = default; - -bool KmerIndex::operator==(const KmerIndex& other) const -{ - return (pimpl_->kmer_to_paths_map == other.pimpl_->kmer_to_paths_map && pimpl_->kmer_len == other.pimpl_->kmer_len); -} - -static string encodePaths(const list& paths) -{ - list path_encodings; - for (const auto& path : paths) - { - path_encodings.push_back(path.encode()); - } - return boost::algorithm::join(path_encodings, ","); -} - -size_t KmerIndex::kmerLength() const { return pimpl_->kmer_len; } - -string KmerIndex::encode() const -{ - list kv_encodings; - for (const auto& kv : pimpl_->kmer_to_paths_map) - { - const string encoding_of_paths = encodePaths(kv.second); - const string kv_encoding = "{" + kv.first + "->" + encoding_of_paths + "}"; - kv_encodings.push_back(kv_encoding); - } - return boost::algorithm::join(kv_encodings, ","); -} - -bool KmerIndex::contains(const std::string& kmer) const -{ - return pimpl_->kmer_to_paths_map.find(kmer) != pimpl_->kmer_to_paths_map.end(); -} - -size_t KmerIndex::numPaths(const std::string& kmer) const -{ - if (!contains(kmer)) - { - return 0; - } - return pimpl_->kmer_to_paths_map.at(kmer).size(); -} - -const list& KmerIndex::getPaths(const std::string& kmer) const { return pimpl_->kmer_to_paths_map.at(kmer); } - -unordered_set KmerIndex::kmers() const -{ - unordered_set kmers; - for (const auto& kv : pimpl_->kmer_to_paths_map) - { - kmers.insert(kv.first); - } - return kmers; -} - -size_t KmerIndex::numUniqueKmersOverlappingNode(NodeId node_id) const -{ - auto node_it = pimpl_->node_kmer_counts.find(node_id); - if (node_it != pimpl_->node_kmer_counts.end()) - { - return node_it->second; - } - return 0; -} - -size_t KmerIndex::numUniqueKmersOverlappingEdge(NodeId from, NodeId to) const -{ - auto edge_it = pimpl_->edge_kmer_counts.find(std::make_pair(from, to)); - if (edge_it != pimpl_->edge_kmer_counts.end()) - { - return edge_it->second; - } - return 0; -} - -std::ostream& operator<<(std::ostream& os, const KmerIndex& kmer_index) -{ - os << kmer_index.encode(); - return os; -} -} diff --git a/thirdparty/graph-tools-master/src/graphcore/Path.cpp b/thirdparty/graph-tools-master/src/graphcore/Path.cpp deleted file mode 100755 index 80db4e6..0000000 --- a/thirdparty/graph-tools-master/src/graphcore/Path.cpp +++ /dev/null @@ -1,576 +0,0 @@ -// -// GraphTools library -// Copyright 2017-2019 Illumina, Inc. -// All rights reserved. -// -// Author: Egor Dolzhenko -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "graphcore/Path.hh" - -#include -#include -#include -#include -#include - -using std::list; -using std::logic_error; -using std::ostream; -using std::set; -using std::shared_ptr; -using std::string; -using std::to_string; -using std::vector; - -namespace graphtools -{ -struct Path::Impl -{ - Impl( - const Graph* new_graph_raw_ptr, int32_t new_start_position, const vector& new_nodes, - int32_t new_end_position) - : graph_raw_ptr(new_graph_raw_ptr) - , start_position(new_start_position) - , end_position(new_end_position) - , nodes(new_nodes) - { - } - void assertValidity() const; - bool isNodePositionValid(NodeId node_id, int32_t position) const; - void assertPositionsOrdered() const; - void assertNonEmpty() const; - void assertFirstNodePosValid() const; - void assertLastNodePosValid() const; - void assertConnected() const; - string encode() const; - void assertThatIndexIsValid(int32_t node_index) const - { - if (node_index < 0 || node_index >= (signed)nodes.size()) - { - const string msg = "Node index " + to_string(node_index) + "is out of bounds for path " + encode(); - throw std::logic_error(msg); - } - } - - bool operator==(const Impl& other) const - { - return (graph_raw_ptr == other.graph_raw_ptr) && (start_position == other.start_position) - && (end_position == other.end_position) && (nodes == other.nodes); - } - - const Graph* const graph_raw_ptr; - int32_t start_position; - int32_t end_position; - vector nodes; -}; - -void Path::Impl::assertValidity() const -{ - assertNonEmpty(); - assertFirstNodePosValid(); - assertLastNodePosValid(); - assertPositionsOrdered(); - assertConnected(); -} - -void Path::Impl::assertPositionsOrdered() const -{ - const bool positionsOrdered = nodes.size() != 1 || start_position <= end_position; - if (!positionsOrdered) - { - throw logic_error("Positions are not ordered"); - } -} - -bool Path::Impl::isNodePositionValid(NodeId node_id, int32_t position) const -{ - if (position < 0) - { - return false; - } - const string& node_seq = graph_raw_ptr->nodeSeq(node_id); - return (unsigned)position <= node_seq.length(); -} - -void Path::Impl::assertNonEmpty() const -{ - if (nodes.empty()) - { - throw logic_error("Path is empty"); - } -} - -void Path::Impl::assertFirstNodePosValid() const -{ - const NodeId first_node_id = nodes.front(); - if (!isNodePositionValid(first_node_id, start_position)) - { - throw logic_error("Position of first node is invalid"); - } -} - -void Path::Impl::assertLastNodePosValid() const -{ - const NodeId last_node_id = nodes.back(); - if (!isNodePositionValid(last_node_id, end_position)) - { - throw logic_error("Position of last node is invalid"); - } -} - -void Path::Impl::assertConnected() const -{ - vector::const_iterator start_iter; - vector::const_iterator end_iter; - for (start_iter = nodes.begin(); start_iter != std::prev(nodes.end()); ++start_iter) - { - end_iter = std::next(start_iter); - if (!graph_raw_ptr->hasEdge(*start_iter, *end_iter)) - { - throw logic_error("Path is not connected"); - } - } -} - -string Path::Impl::encode() const -{ - string path_encoding; - - size_t node_index = 0; - const size_t last_index = nodes.size() - 1; - for (NodeId node_id : nodes) - { - const string node_name = to_string(node_id); - string node_encoding; - if (node_index == 0) // Encoding first node. - { - node_encoding = "(" + node_name + "@" + to_string(start_position) + ")"; - } - if (node_index == last_index) // Encoding last node. - { - node_encoding += "-(" + node_name + "@" + to_string(end_position) + ")"; - } - if (node_index != 0 && node_index != last_index) // Encoding intermediate node. - { - node_encoding = "-(" + node_name + ")"; - } - path_encoding += node_encoding; - ++node_index; - } - - return path_encoding; -} - -string Path::encode() const { return pimpl_->encode(); } - -Path::Path(const Graph* graph_raw_ptr, int32_t start_position, const vector& nodes, int32_t end_position) - : pimpl_(new Impl(graph_raw_ptr, start_position, nodes, end_position)) -{ - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to create path " + encode() + ": " + e.what()); - } -} - -Path::~Path() = default; - -Path::Path(const Path& other) - : pimpl_(new Impl(*other.pimpl_)) -{ -} - -Path::Path(Path&& other) noexcept - : pimpl_(std::move(other.pimpl_)) -{ -} - -Path& Path::operator=(const Path& other) -{ - if (this != &other) - { - pimpl_.reset(new Impl(*other.pimpl_)); - } - return *this; -} - -Path::const_iterator Path::begin() const { return pimpl_->nodes.begin(); } -Path::const_iterator Path::end() const { return pimpl_->nodes.end(); } - -Path& Path::operator=(Path&& other) noexcept -{ - pimpl_ = std::move(other.pimpl_); - return *this; -} - -int32_t Path::startPosition() const { return pimpl_->start_position; } -int32_t Path::endPosition() const { return pimpl_->end_position; } -const Graph* Path::graphRawPtr() const { return pimpl_->graph_raw_ptr; } - -vector const& Path::nodeIds() const { return pimpl_->nodes; } - -size_t Path::numNodes() const { return pimpl_->nodes.size(); } - -NodeId Path::getNodeIdByIndex(size_t node_index) const { return pimpl_->nodes[node_index]; } - -bool Path::checkOverlapWithNode(NodeId node_id) const -{ - const vector& nodes = pimpl_->nodes; - return std::find(nodes.begin(), nodes.end(), node_id) != nodes.end(); -} - -int32_t Path::getStartPositionOnNodeByIndex(size_t node_index) const -{ - pimpl_->assertThatIndexIsValid(static_cast(node_index)); - - if (node_index == 0) - { - return startPosition(); - } - - return 0; -} - -int32_t Path::getEndPositionOnNodeByIndex(size_t node_index) const -{ - pimpl_->assertThatIndexIsValid(static_cast(node_index)); - - if (node_index == numNodes() - 1) - { - return endPosition(); - } - - const int32_t node_id = pimpl_->nodes[node_index]; - const size_t node_length = graphRawPtr()->nodeSeq(static_cast(node_id)).length(); - - return node_length; -} - -size_t Path::getNodeOverlapLengthByIndex(size_t node_index) const -{ - pimpl_->assertThatIndexIsValid(static_cast(node_index)); - const int32_t node_id = pimpl_->nodes[node_index]; - const size_t node_length = graphRawPtr()->nodeSeq(static_cast(node_id)).length(); - auto length_on_node = (int32_t)node_length; // This is the length of all intermediate nodes. - - const bool is_first_node = node_index == 0; - const bool is_last_node = node_index + 1 == numNodes(); - - if (is_first_node && is_last_node) - { - length_on_node = pimpl_->end_position - pimpl_->start_position; - } - else if (is_first_node) - { - length_on_node = static_cast(node_length - pimpl_->start_position); - } - else if (is_last_node) - { - length_on_node = pimpl_->end_position; - } - - return static_cast(length_on_node); -} - -int32_t Path::getDistanceFromPathStart(NodeId node, int32_t offset) const -{ - size_t n = 0; - int32_t distance = 0; - bool found = false; - while (n < numNodes()) - { - const auto node_id = pimpl_->nodes[n]; - const int32_t node_start = n == 0 ? pimpl_->start_position : 0; - const int32_t node_end - = n == numNodes() - 1 ? pimpl_->end_position : (int32_t)pimpl_->graph_raw_ptr->nodeSeq(node_id).size() - 1; - - if (node_id == node && offset >= node_start && offset <= node_end) - { - distance += offset - node_start; - found = true; - break; - } - - distance += node_end - node_start + 1; - n++; - } - if (!found) - { - throw std::logic_error(std::to_string(node) + "@" + std::to_string(offset) + " is not on path " + encode()); - } - return distance; -} - -size_t Path::length() const -{ - size_t path_length = 0; - for (int32_t node_index = 0; node_index != (signed)pimpl_->nodes.size(); ++node_index) - { - path_length += getNodeOverlapLengthByIndex(static_cast(node_index)); - } - - return path_length; -} - -string Path::getNodeSeq(size_t node_index) const -{ - auto node_id = static_cast(pimpl_->nodes[node_index]); - const string& sequence = pimpl_->graph_raw_ptr->nodeSeq(static_cast(node_id)); - - if (node_index == 0) - { - const size_t node_overlap_len = getNodeOverlapLengthByIndex(node_index); - return sequence.substr(static_cast(pimpl_->start_position), node_overlap_len); - } - else if ((size_t)node_index == pimpl_->nodes.size() - 1) - { - const size_t node_overlap_len = getNodeOverlapLengthByIndex(node_index); - return sequence.substr(0, node_overlap_len); - } - else - { - return sequence; - } -} - -string Path::seq() const -{ - string path_seq; - size_t node_index = 0; - for (NodeId node_id : pimpl_->nodes) - { - string node_seq = pimpl_->graph_raw_ptr->nodeSeq(node_id); - if (node_index == 0) - { - node_seq = node_seq.substr(static_cast(pimpl_->start_position)); - } - - if (node_index == pimpl_->nodes.size() - 1) - { - const int32_t end_node_start = pimpl_->nodes.size() == 1 ? pimpl_->start_position : 0; - const int32_t segment_len = pimpl_->end_position - end_node_start; - node_seq = node_seq.substr(0, (unsigned long)segment_len); - } - - path_seq += node_seq; - ++node_index; - } - return path_seq; -} - -bool Path::operator==(const Path& other) const { return *pimpl_ == *other.pimpl_; } - -ostream& operator<<(ostream& os, const Path& path) { return os << path.encode(); } - -void Path::shiftStartAlongNode(int32_t shift_len) -{ - pimpl_->start_position -= shift_len; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to shift start of " + encode() + " by " + to_string(shift_len) + ": " + e.what()); - } -} - -void Path::shiftEndAlongNode(int32_t shift_len) -{ - pimpl_->end_position += shift_len; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to shift end of " + encode() + " by " + to_string(shift_len) + ": " + e.what()); - } -} - -void Path::extendStartToNode(NodeId node_id) -{ - pimpl_->nodes.insert(pimpl_->nodes.begin(), node_id); - const auto new_node_seq_len = static_cast(pimpl_->graph_raw_ptr->nodeSeq(node_id).length()); - pimpl_->start_position = new_node_seq_len; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to extend " + encode() + " to node " + to_string(node_id) + ": " + e.what()); - } -} - -void Path::extendStartToIncludeNode(NodeId node_id) -{ - pimpl_->nodes.insert(pimpl_->nodes.begin(), node_id); - pimpl_->start_position = 0; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to extend " + encode() + " to node " + to_string(node_id) + ": " + e.what()); - } -} - -void Path::removeStartNode() -{ - pimpl_->nodes.erase(pimpl_->nodes.begin()); - pimpl_->start_position = 0; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to remove start node of " + encode() + ": " + e.what()); - } -} - -void Path::removeZeroLengthStart() -{ - if (numNodes() > 1 && getNodeOverlapLengthByIndex(0) == 0) - { - removeStartNode(); - } -} - -void Path::removeZeroLengthEnd() -{ - const size_t index_of_last_node = numNodes() - 1; - if (numNodes() > 1 && getNodeOverlapLengthByIndex(index_of_last_node) == 0) - { - removeEndNode(); - } -} - -void Path::extendEndToNode(NodeId node_id) -{ - pimpl_->nodes.push_back(node_id); - pimpl_->end_position = 0; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to extend " + encode() + " right to node " + to_string(node_id) + ": " + e.what()); - } -} - -void Path::extendEndToIncludeNode(NodeId node_id) -{ - pimpl_->nodes.push_back(node_id); - const auto new_node_seq_len = static_cast(pimpl_->graph_raw_ptr->nodeSeq(node_id).length()); - pimpl_->end_position = new_node_seq_len; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to extend " + encode() + " right to node " + to_string(node_id) + ": " + e.what()); - } -} - -void Path::removeEndNode() -{ - pimpl_->nodes.erase(pimpl_->nodes.end() - 1); - NodeId new_last_node_id = pimpl_->nodes.back(); - auto new_last_node_len = static_cast(pimpl_->graph_raw_ptr->nodeSeq(new_last_node_id).length()); - pimpl_->end_position = new_last_node_len; - - try - { - pimpl_->assertValidity(); - } - catch (const std::exception& e) - { - throw logic_error("Unable to remove end node of " + encode() + ": " + e.what()); - } -} - -void Path::shrinkStartBy(int32_t shrink_len) -{ - const int32_t node_len_left = getNodeOverlapLengthByIndex(0); - - if (shrink_len <= node_len_left) - { - shiftStartAlongNode(-shrink_len); - removeZeroLengthStart(); - } - else - { - removeStartNode(); - - const int32_t leftover_len = shrink_len - node_len_left; - shrinkStartBy(leftover_len); - } -} - -void Path::shrinkEndBy(int32_t shrink_len) -{ - const int32_t node_len_left = pimpl_->end_position; - - if (shrink_len <= node_len_left) - { - shiftEndAlongNode(-shrink_len); - removeZeroLengthEnd(); - } - else - { - removeEndNode(); - - const int32_t leftover_len = shrink_len - node_len_left; - shrinkEndBy(leftover_len); - } -} - -void Path::shrinkBy(int32_t start_shrink_len, int32_t end_shrink_len) -{ - shrinkStartBy(start_shrink_len); - shrinkEndBy(end_shrink_len); -} - -bool Path::operator<(const Path& other) const -{ - if (pimpl_->start_position != other.pimpl_->start_position) - { - return pimpl_->start_position < other.pimpl_->start_position; - } - - if (pimpl_->nodes != other.pimpl_->nodes) - { - return pimpl_->nodes < other.pimpl_->nodes; - } - - return pimpl_->end_position < other.pimpl_->end_position; -} -} diff --git a/thirdparty/spdlog/.gitattributes b/thirdparty/spdlog/.gitattributes deleted file mode 100644 index fe505b2..0000000 --- a/thirdparty/spdlog/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=false diff --git a/thirdparty/spdlog/.gitignore b/thirdparty/spdlog/.gitignore deleted file mode 100644 index 1b45228..0000000 --- a/thirdparty/spdlog/.gitignore +++ /dev/null @@ -1,83 +0,0 @@ -# Auto generated files -build/* -*.slo -*.lo -*.o -*.obj -*.suo -*.tlog -*.ilk -*.log -*.pdb -*.idb -*.iobj -*.ipdb -*.opensdf -*.sdf - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Codelite -.codelite - -# KDevelop -*.kdev4 - -# .orig files -*.orig - -# example files -example/* -!example/example.cpp -!example/bench.cpp -!example/utils.h -!example/Makefile* -!example/example.sln -!example/example.vcxproj -!example/CMakeLists.txt -!example/meson.build -!example/multisink.cpp -!example/jni - -# generated files -generated - -# Cmake -CMakeCache.txt -CMakeFiles -CMakeScripts -Makefile -cmake_install.cmake -install_manifest.txt -/tests/tests.VC.VC.opendb -/tests/tests.VC.db -/tests/tests -/tests/logs/* - -# idea -.idea/ -cmake-build-*/ -*.db -*.ipch -*.filters -*.db-wal -*.opendb -*.db-shm -*.vcxproj -*.tcl -*.user -*.sln diff --git a/thirdparty/spdlog/.travis.yml b/thirdparty/spdlog/.travis.yml deleted file mode 100644 index b3137c6..0000000 --- a/thirdparty/spdlog/.travis.yml +++ /dev/null @@ -1,111 +0,0 @@ -# Adapted from various sources, including: -# - Louis Dionne's Hana: https://github.com/ldionne/hana -# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit -# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3 -sudo: required -language: cpp - -# gcc 4.8 -addons: &gcc48 - apt: - packages: - - g++-4.8 - sources: - - ubuntu-toolchain-r-test - -# gcc 7.0 -addons: &gcc7 - apt: - packages: - - g++-7 - sources: - - ubuntu-toolchain-r-test - -# Clang 3.5 -addons: &clang35 - apt: - packages: - - clang-3.5 - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.5 - -# Clang 7.0 -addons: &clang70 - apt: - packages: - - clang-7 - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-7 - - - -matrix: - include: - # Test gcc-4.8: C++11, Build=Debug/Release - - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 - os: linux - addons: *gcc48 - - - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 - os: linux - addons: *gcc48 - - - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 - os: linux - addons: *gcc7 - - # Test clang-3.5: C++11, Build=Debug/Release - - env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 - os: linux - addons: *clang35 - - - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 - os: linux - addons: *clang35 - - # Test clang-7.0: C++11, Build=Debug, ASAN=On - - env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off - dist: bionic - - - env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off - dist: bionic - - # osx - - env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off - os: osx - - - -before_script: - - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi - - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi - - which $CXX - - which $CC - - $CXX --version - - cmake --version - -script: - - cd ${TRAVIS_BUILD_DIR} - - mkdir -p build && cd build - - | - cmake .. \ - --warn-uninitialized \ - -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ - -DCMAKE_CXX_STANDARD=$CPP \ - -DSPDLOG_BUILD_EXAMPLE=ON \ - -DSPDLOG_BUILD_EXAMPLE_HO=ON \ - -DSPDLOG_BUILD_BENCH=OFF \ - -DSPDLOG_BUILD_TESTS=ON \ - -DSPDLOG_BUILD_TESTS_HO=OFf \ - -DSPDLOG_SANITIZE_ADDRESS=$ASAN - - - make VERBOSE=1 -j2 - - ctest -j2 --output-on-failure - - - -notifications: - email: false diff --git a/thirdparty/spdlog/CMakeLists.txt b/thirdparty/spdlog/CMakeLists.txt deleted file mode 100644 index 508a0b6..0000000 --- a/thirdparty/spdlog/CMakeLists.txt +++ /dev/null @@ -1,290 +0,0 @@ -# Copyright(c) 2019 spdlog authors -# Distributed under the MIT License (http://opensource.org/licenses/MIT) - -cmake_minimum_required(VERSION 3.2) - -#--------------------------------------------------------------------------------------- -# Start spdlog project -#--------------------------------------------------------------------------------------- -include(GNUInstallDirs) -include(cmake/utils.cmake) -include(cmake/ide.cmake) - -spdlog_extract_version() - -#--------------------------------------------------------------------------------------- -# Set default build to release -#--------------------------------------------------------------------------------------- -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) -endif() - -project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX) -message(STATUS "Build spdlog: ${SPDLOG_VERSION}") - -#--------------------------------------------------------------------------------------- -# Compiler config -#--------------------------------------------------------------------------------------- -if (NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 11) - set(CMAKE_CXX_STANDARD_REQUIRED ON) -endif() - -set(CMAKE_CXX_EXTENSIONS OFF) - -if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN") - set(CMAKE_CXX_EXTENSIONS ON) -endif() - - -#--------------------------------------------------------------------------------------- -# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog -#--------------------------------------------------------------------------------------- -# Check if spdlog is being used directly or via add_subdirectory, but allow overriding -if (NOT DEFINED SPDLOG_MASTER_PROJECT) - if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(SPDLOG_MASTER_PROJECT ON) - else() - set(SPDLOG_MASTER_PROJECT OFF) - endif() -endif () - -# build shared option -if(NOT WIN32) - option(SPDLOG_BUILD_SHARED "Build shared library" OFF) -endif() - -# example options -option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT}) -option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF) - -# testing options -option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT}) -option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF) - -# bench options -option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF) - -# sanitizer options -option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF) - -# install options -option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT}) -option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) -option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF) -option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) - -if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) - message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") -endif() - -# misc tweakme options -if(WIN32) - option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF) - option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF) -endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - option(SPDLOG_CLOCK_COARSE "Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF) -endif() - -option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF) -option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF) -option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF) -option(SPDLOG_NO_ATOMIC_LEVELS "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" OFF) - -find_package(Threads REQUIRED) -message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) -#--------------------------------------------------------------------------------------- -# Static/Shared library (shared not supported in windows yet) -#--------------------------------------------------------------------------------------- -set(SPDLOG_SRCS - src/spdlog.cpp - src/stdout_sinks.cpp - src/color_sinks.cpp - src/file_sinks.cpp - src/async.cpp) - - -if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) - list(APPEND SPDLOG_SRCS src/fmt.cpp) -endif() - -if (SPDLOG_BUILD_SHARED) - if(WIN32) - message(FATAL_ERROR "spdlog shared lib is not yet supported under windows") - endif() - add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) -else() - add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) -endif() - -add_library(spdlog::spdlog ALIAS spdlog) - -target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB) -target_include_directories(spdlog PUBLIC - "$" - "$") -target_link_libraries(spdlog PUBLIC Threads::Threads) -spdlog_enable_warnings(spdlog) - -set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR}) -set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d) - -#--------------------------------------------------------------------------------------- -# Header only version -#--------------------------------------------------------------------------------------- -add_library(spdlog_header_only INTERFACE) -add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only) - -target_include_directories(spdlog_header_only INTERFACE - "$" - "$") -target_link_libraries(spdlog_header_only INTERFACE Threads::Threads) - - -#--------------------------------------------------------------------------------------- -# Use fmt package if using external fmt -#--------------------------------------------------------------------------------------- -if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) - if (NOT TARGET fmt::fmt) - find_package(fmt REQUIRED) - endif () - target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL) - - # use external fmt-header-nly - if(SPDLOG_FMT_EXTERNAL_HO) - target_link_libraries(spdlog PUBLIC fmt::fmt-header-only) - target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only) - else() # use external compile fmt - target_link_libraries(spdlog PUBLIC fmt::fmt) - target_link_libraries(spdlog_header_only INTERFACE fmt::fmt) - endif() - - set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config -endif() - -#--------------------------------------------------------------------------------------- -# Misc definitions according to tweak options -#--------------------------------------------------------------------------------------- -if(SPDLOG_WCHAR_SUPPORT) - target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT) - endif() - - if(SPDLOG_WCHAR_FILENAMES) - target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_FILENAMES) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_FILENAMES) - endif() - - if(SPDLOG_NO_EXCEPTIONS) - target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS) - - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS) - - if(NOT MSVC) - target_compile_options(spdlog PRIVATE -fno-exceptions) - endif() -endif() - -if(SPDLOG_CLOCK_COARSE) - target_compile_definitions(spdlog PRIVATE SPDLOG_CLOCK_COARSE) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_CLOCK_COARSE) -endif() - -if(SPDLOG_PREVENT_CHILD_FD) - target_compile_definitions(spdlog PRIVATE SPDLOG_PREVENT_CHILD_FD) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_PREVENT_CHILD_FD) -endif() - -if(SPDLOG_NO_THREAD_ID) - target_compile_definitions(spdlog PRIVATE SPDLOG_NO_THREAD_ID) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_THREAD_ID) -endif() - -if(SPDLOG_NO_TLS) - target_compile_definitions(spdlog PRIVATE SPDLOG_NO_TLS) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_TLS) -endif() - -if(SPDLOG_NO_ATOMIC_LEVELS) - target_compile_definitions(spdlog PUBLIC SPDLOG_NO_ATOMIC_LEVELS) - target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS) -endif() - - -#--------------------------------------------------------------------------------------- -# Build binaries -#--------------------------------------------------------------------------------------- -if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO) - message(STATUS "Generating examples") - add_subdirectory(example) -endif() - -if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO) - message(STATUS "Generating tests") - enable_testing() - add_subdirectory(tests) -endif() - -if(SPDLOG_BUILD_BENCH) - message(STATUS "Generating benchmarks") - add_subdirectory(bench) -endif() - -#--------------------------------------------------------------------------------------- -# Install -#--------------------------------------------------------------------------------------- -if (SPDLOG_INSTALL) - message(STATUS "Generating install") - set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in") - set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake") - set(config_targets_file "spdlogConfigTargets.cmake") - set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake") - set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/spdlog") - set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig") - set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc") - - #--------------------------------------------------------------------------------------- - # Include files - #--------------------------------------------------------------------------------------- - install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE) - install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}") - - if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) - install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/ - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/") - endif() - - #--------------------------------------------------------------------------------------- - # Install pkg-config file - #--------------------------------------------------------------------------------------- - get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS) - string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}") - string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}") - configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY) - install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}") - - #--------------------------------------------------------------------------------------- - # Install CMake config files - #--------------------------------------------------------------------------------------- - install(EXPORT spdlog - DESTINATION ${export_dest_dir} - NAMESPACE spdlog:: - FILE ${config_targets_file}) - - include(CMakePackageConfigHelpers) - configure_file("${project_config_in}" "${project_config_out}" @ONLY) - - write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion) - install(FILES - "${project_config_out}" - "${version_config_file}" DESTINATION "${export_dest_dir}") - - #--------------------------------------------------------------------------------------- - # Support creation of installable packages - #--------------------------------------------------------------------------------------- - include(cmake/spdlogCPack.cmake) - -endif () - diff --git a/thirdparty/spdlog/INSTALL b/thirdparty/spdlog/INSTALL deleted file mode 100644 index 787bc3f..0000000 --- a/thirdparty/spdlog/INSTALL +++ /dev/null @@ -1,24 +0,0 @@ -Header only version: -================================================================== -Just copy the files to your build tree and use a C++11 compiler. -Or use CMake: - add_executable(example_header_only example.cpp) - target_link_libraries(example_header_only spdlog::spdlog_header_only) - - -Compiled library version: -================================================================== -CMake: - add_executable(example example.cpp) - target_link_libraries(example spdlog::spdlog) - -Or copy src/spdlog.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler. - -Tested on: -gcc 4.8.1 and above -clang 3.5 -Visual Studio 2013 - - - - diff --git a/thirdparty/spdlog/LICENSE b/thirdparty/spdlog/LICENSE deleted file mode 100644 index 4b43e06..0000000 --- a/thirdparty/spdlog/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Gabi Melman. - -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. - diff --git a/thirdparty/spdlog/README.md b/thirdparty/spdlog/README.md deleted file mode 100644 index c8f1056..0000000 --- a/thirdparty/spdlog/README.md +++ /dev/null @@ -1,357 +0,0 @@ -# spdlog - -Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) - - - -## Install -#### Header only version -Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. - -#### Static lib version (recommended - much faster compile times) -```console -$ git clone https://github.com/gabime/spdlog.git -$ cd spdlog && mkdir build && cd build -$ cmake .. && make -j -``` - - see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use. - -## Platforms - * Linux, FreeBSD, OpenBSD, Solaris, AIX - * Windows (msvc 2013+, cygwin) - * macOS (clang 3.5+) - * Android - -## Package managers: -* Homebrew: `brew install spdlog` -* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean` -* Fedora: `yum install spdlog` -* Gentoo: `emerge dev-libs/spdlog` -* Arch Linux: `yaourt -S spdlog-git` -* vcpkg: `vcpkg install spdlog` -* conan: `spdlog/[>=1.4.1]` - - -## Features -* Very fast (see [benchmarks](#benchmarks) below). -* Headers only, just copy and use. Or use as a compiled library. -* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library. -* **New!** [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand. -* Fast asynchronous mode (optional) -* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. -* Multi/Single threaded loggers. -* Various log targets: - * Rotating log files. - * Daily log files. - * Console logging (colors supported). - * syslog. - * Windows debugger (```OutputDebugString(..)```) - * Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). -* Severity based filtering - threshold levels can be modified in runtime as well as in compile time. - - -## Usage samples - -#### Basic usage -```c++ -#include "spdlog/spdlog.h" -#include "spdlog/sinks/basic_file_sink.h" - -int main() -{ - spdlog::info("Welcome to spdlog!"); - spdlog::error("Some error message with arg: {}", 1); - - spdlog::warn("Easy padding in numbers like {:08d}", 12); - spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); - spdlog::info("Support for floats {:03.2f}", 1.23456); - spdlog::info("Positional args are {1} {0}..", "too", "supported"); - spdlog::info("{:<30}", "left aligned"); - - spdlog::set_level(spdlog::level::debug); // Set global log level to debug - spdlog::debug("This message should be displayed.."); - - // change log pattern - spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); - - // Compile time log levels - // define SPDLOG_ACTIVE_LEVEL to desired level - SPDLOG_TRACE("Some trace message with param {}", 42); - SPDLOG_DEBUG("Some debug message"); - - // Set the default logger to file logger - auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt"); - spdlog::set_default_logger(file_logger); -} -``` -#### create stdout/stderr logger object -```c++ -#include "spdlog/spdlog.h" -#include "spdlog/sinks/stdout_color_sinks.h" -void stdout_example() -{ - // create color multi threaded logger - auto console = spdlog::stdout_color_mt("console"); - auto err_logger = spdlog::stderr_color_mt("stderr"); - spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); -} -``` ---- -#### Basic file logger -```c++ -#include "spdlog/sinks/basic_file_sink.h" -void basic_logfile_example() -{ - try - { - auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); - } - catch (const spdlog::spdlog_ex &ex) - { - std::cout << "Log init failed: " << ex.what() << std::endl; - } -} -``` ---- -#### Rotating files -```c++ -#include "spdlog/sinks/rotating_file_sink.h" -void rotating_example() -{ - // Create a file rotating logger with 5mb size max and 3 rotated files - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); -} -``` - ---- -#### Daily files -```c++ - -#include "spdlog/sinks/daily_file_sink.h" -void daily_example() -{ - // Create a daily logger - a new file is created every day on 2:30am - auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); -} - -``` - ---- -#### Backtrace support -```c++ -// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand. -// When needed, call dump_backtrace() to see them -spdlog::enable_backtrace(32); // create ring buffer with capacity of 32 messages -// or my_logger->enable_backtrace(32).. -for(int i = 0; i < 100; i++) -{ - spdlog::debug("Backtrace message {}", i); // not logged yet.. -} -// e.g. if some error happened: -spdlog::dump_backtrace(); // log them now! show the last 32 messages - -// or my_logger->dump_backtrace(32).. -``` - ---- -#### Periodic flush -```c++ -// periodically flush all *registered* loggers every 3 seconds: -// warning: only use if all your loggers are thread safe ("_mt" loggers) -spdlog::flush_every(std::chrono::seconds(3)); - -``` - ---- -#### Log binary data in hex -```c++ -// many types of std::container types can be used. -// ranges are supported too. -// format flags: -// {:X} - print in uppercase. -// {:s} - don't separate each byte with space. -// {:p} - don't print the position on each line start. -// {:n} - don't split the output to lines. - -#include "spdlog/fmt/bin_to_hex.h" - -void binary_example() -{ - auto console = spdlog::get("console"); - std::array buf; - console->info("Binary example: {}", spdlog::to_hex(buf)); - console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); - // more examples: - // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); - // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); - // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); -} - -``` - ---- -#### Logger with multi sinks - each with different format and log level -```c++ - -// create logger with 2 targets with different log levels and formats. -// the console will show only warnings or errors, while the file will log all. -void multi_sink_example() -{ - auto console_sink = std::make_shared(); - console_sink->set_level(spdlog::level::warn); - console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); - - auto file_sink = std::make_shared("logs/multisink.txt", true); - file_sink->set_level(spdlog::level::trace); - - spdlog::logger logger("multi_sink", {console_sink, file_sink}); - logger.set_level(spdlog::level::debug); - logger.warn("this should appear in both console and file"); - logger.info("this message should not appear in the console, only in the file"); -} -``` - ---- -#### Asynchronous logging -```c++ -#include "spdlog/async.h" -#include "spdlog/sinks/basic_file_sink.h" -void async_example() -{ - // default thread pool settings can be modified *before* creating the async logger: - // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. - auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); - // alternatively: - // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); -} - -``` - ---- -#### Asynchronous logger with multi sinks -```c++ -#include "spdlog/sinks/stdout_color_sinks.h" -#include "spdlog/sinks/rotating_file_sink.h" - -void multi_sink_example2() -{ - spdlog::init_thread_pool(8192, 1); - auto stdout_sink = std::make_shared(); - auto rotating_sink = std::make_shared("mylog.txt", 1024*1024*10, 3); - std::vector sinks {stdout_sink, rotating_sink}; - auto logger = std::make_shared("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); - spdlog::register_logger(logger); -} -``` - ---- -#### User defined types -```c++ -// user defined types logging by implementing operator<< -#include "spdlog/fmt/ostr.h" // must be included -struct my_type -{ - int i; - template - friend OStream &operator<<(OStream &os, const my_type &c) - { - return os << "[my_type i=" << c.i << "]"; - } -}; - -void user_defined_example() -{ - spdlog::get("console")->info("user defined type: {}", my_type{14}); -} - -``` ---- -#### Custom error handler -```c++ -void err_handler_example() -{ - // can be set globally or per logger(logger->set_error_handler(..)) - spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); }); - spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); -} - -``` ---- -#### syslog -```c++ -#include "spdlog/sinks/syslog_sink.h" -void syslog_example() -{ - std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); - syslog_logger->warn("This is warning that will end up in syslog."); -} -``` ---- -#### Android example -```c++ -#include "spdlog/sinks/android_sink.h" -void android_example() -{ - std::string tag = "spdlog-android"; - auto android_logger = spdlog::android_logger_mt("android", tag); - android_logger->critical("Use \"adb shell logcat\" to view this message."); -} -``` - -## Benchmarks - -Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz - -#### Synchronous mode -``` -[info] ************************************************************** -[info] Single thread, 1,000,000 iterations -[info] ************************************************************** -[info] basic_st Elapsed: 0.17 secs 5,777,626/sec -[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec -[info] daily_st Elapsed: 0.20 secs 5,062,659/sec -[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec -[info] ************************************************************** -[info] C-string (400 bytes). Single thread, 1,000,000 iterations -[info] ************************************************************** -[info] basic_st Elapsed: 0.41 secs 2,412,483/sec -[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec -[info] daily_st Elapsed: 0.42 secs 2,393,298/sec -[info] null_st Elapsed: 0.04 secs 27,446,957/sec -[info] ************************************************************** -[info] 10 threads, competing over the same logger object, 1,000,000 iterations -[info] ************************************************************** -[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec -[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec -[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec -[info] null_mt Elapsed: 0.16 secs 6,272,758/sec -``` -#### ASynchronous mode -``` -[info] ------------------------------------------------- -[info] Messages : 1,000,000 -[info] Threads : 10 -[info] Queue : 8,192 slots -[info] Queue memory : 8,192 x 272 = 2,176 KB -[info] ------------------------------------------------- -[info] -[info] ********************************* -[info] Queue Overflow Policy: block -[info] ********************************* -[info] Elapsed: 1.70784 secs 585,535/sec -[info] Elapsed: 1.69805 secs 588,910/sec -[info] Elapsed: 1.7026 secs 587,337/sec -[info] -[info] ********************************* -[info] Queue Overflow Policy: overrun -[info] ********************************* -[info] Elapsed: 0.372816 secs 2,682,285/sec -[info] Elapsed: 0.379758 secs 2,633,255/sec -[info] Elapsed: 0.373532 secs 2,677,147/sec - -``` - -## Documentation -Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. diff --git a/thirdparty/spdlog/appveyor.yml b/thirdparty/spdlog/appveyor.yml deleted file mode 100644 index d76d379..0000000 --- a/thirdparty/spdlog/appveyor.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: 1.0.{build} -image: Visual Studio 2017 -environment: - matrix: - - GENERATOR: '"Visual Studio 14 2015"' - BUILD_TYPE: Debug - WCHAR: 'OFF' - - GENERATOR: '"Visual Studio 14 2015"' - BUILD_TYPE: Release - WCHAR: 'ON' - - GENERATOR: '"Visual Studio 14 2015 Win64"' - BUILD_TYPE: Debug - WCHAR: 'ON' - - GENERATOR: '"Visual Studio 14 2015 Win64"' - BUILD_TYPE: Release - WCHAR: 'ON' - - GENERATOR: '"Visual Studio 15 2017 Win64"' - BUILD_TYPE: Debug - WCHAR: 'ON' - - GENERATOR: '"Visual Studio 15 2017 Win64"' - BUILD_TYPE: Release - WCHAR: 'OFf' -build_script: -- cmd: >- - set - - mkdir build - - cd build - - set PATH=%PATH%:C:\Program Files\Git\usr\bin - - cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF - - cmake --build . --config %BUILD_TYPE% - -test_script: -- ctest -VV -C "%BUILD_TYPE%" diff --git a/thirdparty/spdlog/bench/CMakeLists.txt b/thirdparty/spdlog/bench/CMakeLists.txt deleted file mode 100644 index 0ea8842..0000000 --- a/thirdparty/spdlog/bench/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright(c) 2019 spdlog authors -# Distributed under the MIT License (http://opensource.org/licenses/MIT) - -cmake_minimum_required(VERSION 3.1) -project(spdlog_bench CXX) - -if(NOT TARGET spdlog) - # Stand-alone build - find_package(spdlog CONFIG REQUIRED) -endif() - -find_package(Threads REQUIRED) -find_package(benchmark CONFIG REQUIRED) - -add_executable(bench bench.cpp) -spdlog_enable_warnings(bench) -target_link_libraries(bench PRIVATE spdlog::spdlog) - -add_executable(async_bench async_bench.cpp) -target_link_libraries(async_bench PRIVATE spdlog::spdlog) - -add_executable(latency latency.cpp) -target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) - -add_executable(formatter-bench formatter-bench.cpp) -target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) diff --git a/thirdparty/spdlog/bench/async_bench.cpp b/thirdparty/spdlog/bench/async_bench.cpp deleted file mode 100644 index fc49cdd..0000000 --- a/thirdparty/spdlog/bench/async_bench.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -// -// bench.cpp : spdlog benchmarks -// -#include "spdlog/spdlog.h" -#include "spdlog/async.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "spdlog/sinks/stdout_color_sinks.h" - -#include "utils.h" -#include -#include -#include -#include -#include - -using namespace std; -using namespace std::chrono; -using namespace spdlog; -using namespace spdlog::sinks; -using namespace utils; - -void bench_mt(int howmany, std::shared_ptr log, int thread_count); - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) // disable fopen warning under msvc -#endif // _MSC_VER - -int count_lines(const char *filename) -{ - int counter = 0; - auto *infile = fopen(filename, "r"); - int ch; - while (EOF != (ch = getc(infile))) - { - if ('\n' == ch) - counter++; - } - fclose(infile); - - return counter; -} - -void verify_file(const char *filename, int expected_count) -{ - spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count); - auto count = count_lines(filename); - if (count != expected_count) - { - spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count); - exit(1); - } - spdlog::info("Line count OK ({:n})\n", count); -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -int main(int argc, char *argv[]) -{ - - int howmany = 1000000; - int queue_size = std::min(howmany + 2, 8192); - int threads = 10; - int iters = 3; - - try - { - spdlog::set_pattern("[%^%l%$] %v"); - if (argc == 1) - { - spdlog::info("Usage: {} ", argv[0]); - return 0; - } - - if (argc > 1) - howmany = atoi(argv[1]); - if (argc > 2) - threads = atoi(argv[2]); - if (argc > 3) - { - queue_size = atoi(argv[3]); - if (queue_size > 500000) - { - spdlog::error("Max queue size allowed: 500,000"); - exit(1); - } - } - - if (argc > 4) - iters = atoi(argv[4]); - - auto slot_size = sizeof(spdlog::details::async_msg); - spdlog::info("-------------------------------------------------"); - spdlog::info("Messages : {:n}", howmany); - spdlog::info("Threads : {:n}", threads); - spdlog::info("Queue : {:n} slots", queue_size); - spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024); - spdlog::info("Total iters : {:n}", iters); - spdlog::info("-------------------------------------------------"); - - const char *filename = "logs/basic_async.log"; - spdlog::info(""); - spdlog::info("*********************************"); - spdlog::info("Queue Overflow Policy: block"); - spdlog::info("*********************************"); - for (int i = 0; i < iters; i++) - { - auto tp = std::make_shared(queue_size, 1); - auto file_sink = std::make_shared(filename, true); - auto logger = std::make_shared("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block); - bench_mt(howmany, std::move(logger), threads); - // verify_file(filename, howmany); - } - - spdlog::info(""); - spdlog::info("*********************************"); - spdlog::info("Queue Overflow Policy: overrun"); - spdlog::info("*********************************"); - // do same test but discard oldest if queue is full instead of blocking - filename = "logs/basic_async-overrun.log"; - for (int i = 0; i < iters; i++) - { - auto tp = std::make_shared(queue_size, 1); - auto file_sink = std::make_shared(filename, true); - auto logger = - std::make_shared("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::overrun_oldest); - bench_mt(howmany, std::move(logger), threads); - } - spdlog::shutdown(); - } - catch (std::exception &ex) - { - std::cerr << "Error: " << ex.what() << std::endl; - perror("Last error"); - return 1; - } - return 0; -} - -void thread_fun(std::shared_ptr logger, int howmany) -{ - for (int i = 0; i < howmany; i++) - { - logger->info("Hello logger: msg number {}", i); - } -} - -void bench_mt(int howmany, std::shared_ptr logger, int thread_count) -{ - using std::chrono::high_resolution_clock; - vector threads; - auto start = high_resolution_clock::now(); - - int msgs_per_thread = howmany / thread_count; - int msgs_per_thread_mod = howmany % thread_count; - for (int t = 0; t < thread_count; ++t) - { - if (t == 0 && msgs_per_thread_mod) - threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod)); - else - threads.push_back(std::thread(thread_fun, logger, msgs_per_thread)); - } - - for (auto &t : threads) - { - t.join(); - }; - - auto delta = high_resolution_clock::now() - start; - auto delta_d = duration_cast>(delta).count(); - spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d)); -} diff --git a/thirdparty/spdlog/bench/bench.cpp b/thirdparty/spdlog/bench/bench.cpp deleted file mode 100644 index 591057b..0000000 --- a/thirdparty/spdlog/bench/bench.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -// -// bench.cpp : spdlog benchmarks -// -#include "spdlog/spdlog.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "spdlog/sinks/daily_file_sink.h" -#include "spdlog/sinks/null_sink.h" -#include "spdlog/sinks/rotating_file_sink.h" - -#include "utils.h" -#include -#include // EXIT_FAILURE -#include -#include -#include - -using namespace std; -using namespace std::chrono; -using namespace spdlog; -using namespace spdlog::sinks; -using namespace utils; - -void bench(int howmany, std::shared_ptr log); -void bench_mt(int howmany, std::shared_ptr log, int thread_count); -void bench_default_api(int howmany, std::shared_ptr log); -void bench_c_string(int howmany, std::shared_ptr log); - -static size_t file_size = 30 * 1024 * 1024; -static size_t rotating_files = 5; - -void bench_threaded_logging(int threads, int iters) -{ - spdlog::info("**************************************************************"); - spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters); - spdlog::info("**************************************************************"); - - auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); - bench_mt(iters, std::move(basic_mt), threads); - auto basic_mt_tracing = spdlog::basic_logger_mt("basic_mt/backtrace-on", "logs/basic_mt.log", true); - basic_mt_tracing->enable_backtrace(32); - bench_mt(iters, std::move(basic_mt_tracing), threads); - - spdlog::info(""); - auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files); - bench_mt(iters, std::move(rotating_mt), threads); - auto rotating_mt_tracing = spdlog::rotating_logger_mt("rotating_mt/backtrace-on", "logs/rotating_mt.log", file_size, rotating_files); - rotating_mt_tracing->enable_backtrace(32); - bench_mt(iters, std::move(rotating_mt_tracing), threads); - - spdlog::info(""); - auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log"); - bench_mt(iters, std::move(daily_mt), threads); - auto daily_mt_tracing = spdlog::daily_logger_mt("daily_mt/backtrace-on", "logs/daily_mt.log"); - daily_mt_tracing->enable_backtrace(32); - bench_mt(iters, std::move(daily_mt_tracing), threads); - - spdlog::info(""); - auto empty_logger = std::make_shared("level-off"); - empty_logger->set_level(spdlog::level::off); - bench(iters, empty_logger); - auto empty_logger_tracing = std::make_shared("level-off/backtrace-on"); - empty_logger_tracing->set_level(spdlog::level::off); - empty_logger_tracing->enable_backtrace(32); - bench(iters, empty_logger_tracing); -} - -void bench_single_threaded(int iters) -{ - spdlog::info("**************************************************************"); - spdlog::info("Single threaded: {:n} messages", iters); - spdlog::info("**************************************************************"); - - auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); - bench(iters, std::move(basic_st)); - - auto basic_st_tracing = spdlog::basic_logger_st("basic_st/backtrace-on", "logs/basic_st.log", true); - bench(iters, std::move(basic_st_tracing)); - - spdlog::info(""); - auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files); - bench(iters, std::move(rotating_st)); - auto rotating_st_tracing = spdlog::rotating_logger_st("rotating_st/backtrace-on", "logs/rotating_st.log", file_size, rotating_files); - rotating_st_tracing->enable_backtrace(32); - bench(iters, std::move(rotating_st_tracing)); - - spdlog::info(""); - auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log"); - bench(iters, std::move(daily_st)); - auto daily_st_tracing = spdlog::daily_logger_st("daily_st/backtrace-on", "logs/daily_st.log"); - daily_st_tracing->enable_backtrace(32); - bench(iters, std::move(daily_st_tracing)); - - spdlog::info(""); - auto empty_logger = std::make_shared("level-off"); - empty_logger->set_level(spdlog::level::off); - bench(iters, empty_logger); - - auto empty_logger_tracing = std::make_shared("level-off/backtrace-on"); - empty_logger_tracing->set_level(spdlog::level::off); - empty_logger_tracing->enable_backtrace(32); - bench(iters, empty_logger_tracing); -} - -int main(int argc, char *argv[]) -{ - spdlog::set_automatic_registration(false); - spdlog::default_logger()->set_pattern("[%^%l%$] %v"); - int iters = 250000; - int threads = 4; - try - { - - if (argc > 1) - iters = atoi(argv[1]); - if (argc > 2) - threads = atoi(argv[2]); - - bench_single_threaded(iters); - bench_threaded_logging(1, iters); - bench_threaded_logging(threads, iters); - } - catch (std::exception &ex) - { - spdlog::error(ex.what()); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -void bench(int howmany, std::shared_ptr log) -{ - using std::chrono::high_resolution_clock; - auto start = high_resolution_clock::now(); - for (auto i = 0; i < howmany; ++i) - { - log->info("Hello logger: msg number {}", i); - } - - auto delta = high_resolution_clock::now() - start; - auto delta_d = duration_cast>(delta).count(); - - spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); - spdlog::drop(log->name()); -} - -void bench_mt(int howmany, std::shared_ptr log, int thread_count) -{ - using std::chrono::high_resolution_clock; - vector threads; - auto start = high_resolution_clock::now(); - for (int t = 0; t < thread_count; ++t) - { - threads.push_back(std::thread([&]() { - for (int j = 0; j < howmany / thread_count; j++) - { - log->info("Hello logger: msg number {}", j); - } - })); - } - - for (auto &t : threads) - { - t.join(); - }; - - auto delta = high_resolution_clock::now() - start; - auto delta_d = duration_cast>(delta).count(); - spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); - spdlog::drop(log->name()); -} - -void bench_default_api(int howmany, std::shared_ptr log) -{ - using std::chrono::high_resolution_clock; - auto orig_default = spdlog::default_logger(); - spdlog::set_default_logger(log); - auto start = high_resolution_clock::now(); - for (auto i = 0; i < howmany; ++i) - { - spdlog::info("Hello logger: msg number {}", i); - } - - auto delta = high_resolution_clock::now() - start; - auto delta_d = duration_cast>(delta).count(); - spdlog::drop(log->name()); - spdlog::set_default_logger(std::move(orig_default)); - spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); -} - -void bench_c_string(int howmany, std::shared_ptr log) -{ - const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " - "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem " - "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed " - "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare " - "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis."; - using std::chrono::high_resolution_clock; - auto orig_default = spdlog::default_logger(); - spdlog::set_default_logger(log); - auto start = high_resolution_clock::now(); - for (auto i = 0; i < howmany; ++i) - { - spdlog::log(level::info, msg); - } - - auto delta = high_resolution_clock::now() - start; - auto delta_d = duration_cast>(delta).count(); - spdlog::drop(log->name()); - spdlog::set_default_logger(std::move(orig_default)); - spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); -} diff --git a/thirdparty/spdlog/bench/formatter-bench.cpp b/thirdparty/spdlog/bench/formatter-bench.cpp deleted file mode 100644 index 405707c..0000000 --- a/thirdparty/spdlog/bench/formatter-bench.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright(c) 2018 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#include "benchmark/benchmark.h" - -#include "spdlog/spdlog.h" -#include "spdlog/details/pattern_formatter.h" - -void bench_formatter(benchmark::State &state, std::string pattern) -{ - auto formatter = spdlog::details::make_unique(pattern); - spdlog::memory_buf_t dest; - std::string logger_name = "logger-name"; - const char *text = "Hello. This is some message with length of 80 "; - - spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"}; - spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text); - - for (auto _ : state) - { - dest.clear(); - formatter->format(msg, dest); - benchmark::DoNotOptimize(dest); - } -} - -void bench_formatters() -{ - // basic patterns(single flag) - std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%"; - std::vector basic_patterns; - for (auto &flag : all_flags) - { - auto pattern = std::string("%") + flag; - benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); - - // pattern = std::string("%16") + flag; - // benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); - // - // // bench center padding - // pattern = std::string("%=16") + flag; - // benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); - } - - // complex patterns - std::vector patterns = { - "[%D %X] [%l] [%n] %v", - "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v", - "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v", - }; - for (auto &pattern : patterns) - { - benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000); - } -} - -int main(int argc, char *argv[]) -{ - - spdlog::set_pattern("[%^%l%$] %v"); - if (argc != 2) - { - spdlog::error("Usage: {} (or \"all\" to bench all)", argv[0]); - exit(1); - } - - std::string pattern = argv[1]; - if (pattern == "all") - { - bench_formatters(); - } - else - { - benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); - } - benchmark::Initialize(&argc, argv); - benchmark::RunSpecifiedBenchmarks(); -} diff --git a/thirdparty/spdlog/bench/latency.cpp b/thirdparty/spdlog/bench/latency.cpp deleted file mode 100644 index cd8717d..0000000 --- a/thirdparty/spdlog/bench/latency.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// -// Copyright(c) 2018 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -// -// latency.cpp : spdlog latency benchmarks -// - -#include "benchmark/benchmark.h" - -#include "spdlog/spdlog.h" -#include "spdlog/async.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "spdlog/sinks/daily_file_sink.h" -#include "spdlog/sinks/null_sink.h" -#include "spdlog/sinks/rotating_file_sink.h" - -void bench_c_string(benchmark::State &state, std::shared_ptr logger) -{ - const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " - "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem " - "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed " - "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare " - "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis."; - - for (auto _ : state) - { - logger->info(msg); - } -} - -void bench_logger(benchmark::State &state, std::shared_ptr logger) -{ - int i = 0; - for (auto _ : state) - { - logger->info("Hello logger: msg number {}...............", ++i); - } -} - -void bench_disabled_macro(benchmark::State &state, std::shared_ptr logger) -{ - int i = 0; - benchmark::DoNotOptimize(i); // prevent unused warnings - benchmark::DoNotOptimize(logger); // prevent unused warnings - for (auto _ : state) - { - SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++); - SPDLOG_DEBUG("Hello logger: msg number {}...............", i++); - } -} - -int main(int argc, char *argv[]) -{ - - using spdlog::sinks::basic_file_sink_mt; - using spdlog::sinks::basic_file_sink_st; - using spdlog::sinks::null_sink_mt; - using spdlog::sinks::null_sink_st; - - size_t file_size = 30 * 1024 * 1024; - size_t rotating_files = 5; - int n_threads = benchmark::CPUInfo::Get().num_cpus; - - // disabled loggers - auto disabled_logger = std::make_shared("bench", std::make_shared()); - disabled_logger->set_level(spdlog::level::off); - benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger); - benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger); - // with backtrace of 64 - auto tracing_disabled_logger = std::make_shared("bench", std::make_shared()); - tracing_disabled_logger->enable_backtrace(64); - benchmark::RegisterBenchmark("disabled-at-runtime/backtrace", bench_logger, tracing_disabled_logger); - - auto null_logger_st = std::make_shared("bench", std::make_shared()); - benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st)); - benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st); - // with backtrace of 64 - auto tracing_null_logger_st = std::make_shared("bench", std::make_shared()); - tracing_null_logger_st->enable_backtrace(64); - benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st); - - // basic_st - auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true); - benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime(); - spdlog::drop("basic_st"); - // with backtrace of 64 - auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true); - tracing_basic_st->enable_backtrace(64); - benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime(); - spdlog::drop("tracing_basic_st"); - - // rotating st - auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files); - benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime(); - spdlog::drop("rotating_st"); - // with backtrace of 64 - auto tracing_rotating_st = - spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files); - benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime(); - spdlog::drop("tracing_rotating_st"); - - // daily st - auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log"); - benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime(); - spdlog::drop("daily_st"); - auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log"); - benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime(); - spdlog::drop("tracing_daily_st"); - - // - // Multi threaded bench, 10 loggers using same logger concurrently - // - auto null_logger_mt = std::make_shared("bench", std::make_shared()); - benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime(); - - // basic_mt - auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true); - benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime(); - spdlog::drop("basic_mt"); - - // rotating mt - auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files); - benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime(); - spdlog::drop("rotating_mt"); - - // daily mt - auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log"); - benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime(); - spdlog::drop("daily_mt"); - - // async - auto queue_size = 1024 * 1024 * 3; - auto tp = std::make_shared(queue_size, 1); - auto async_logger = std::make_shared( - "async_logger", std::make_shared(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest); - benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime(); - - auto async_logger_tracing = std::make_shared( - "async_logger_tracing", std::make_shared(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest); - async_logger_tracing->enable_backtrace(32); - benchmark::RegisterBenchmark("async_logger/tracing", bench_logger, async_logger_tracing)->Threads(n_threads)->UseRealTime(); - - benchmark::Initialize(&argc, argv); - benchmark::RunSpecifiedBenchmarks(); -} diff --git a/thirdparty/spdlog/bench/meson.build b/thirdparty/spdlog/bench/meson.build deleted file mode 100644 index c877b6a..0000000 --- a/thirdparty/spdlog/bench/meson.build +++ /dev/null @@ -1,14 +0,0 @@ -benchmark = dependency('benchmark') - -bench_matrix = [ - ['bench', [spdlog_dep], []], - ['async_bench', [spdlog_dep], []], - ['formatter-bench', [spdlog_dep, benchmark], ['all']], - ['latency', [spdlog_dep, benchmark], []], -] - -foreach i : bench_matrix - bench_exe = executable(i[0], i[0] + '.cpp', dependencies: i[1]) - benchmark('bench_' + i[0], bench_exe, args: i[2]) -endforeach - diff --git a/thirdparty/spdlog/bench/utils.h b/thirdparty/spdlog/bench/utils.h deleted file mode 100644 index 9161012..0000000 --- a/thirdparty/spdlog/bench/utils.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include - -namespace utils { - -template -inline std::string format(const T &value) -{ - static std::locale loc(""); - std::stringstream ss; - ss.imbue(loc); - ss << value; - return ss.str(); -} - -template<> -inline std::string format(const double &value) -{ - static std::locale loc(""); - std::stringstream ss; - ss.imbue(loc); - ss << std::fixed << std::setprecision(1) << value; - return ss.str(); -} - -} // namespace utils diff --git a/thirdparty/spdlog/cmake/ide.cmake b/thirdparty/spdlog/cmake/ide.cmake deleted file mode 100644 index 27472c3..0000000 --- a/thirdparty/spdlog/cmake/ide.cmake +++ /dev/null @@ -1,18 +0,0 @@ -#--------------------------------------------------------------------------------------- -# IDE support for headers -#--------------------------------------------------------------------------------------- -set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include") - -file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h") -file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h") -file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h") -file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h") -file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h") -set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} ${SPDLOG_FMT_BUNDELED_HEADERS}) - -source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS}) -source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS}) -source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS}) -source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS}) -source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS}) - diff --git a/thirdparty/spdlog/cmake/spdlog.pc.in b/thirdparty/spdlog/cmake/spdlog.pc.in deleted file mode 100644 index 861707c..0000000 --- a/thirdparty/spdlog/cmake/spdlog.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ - -Name: lib@PROJECT_NAME@ -Description: Fast C++ logging library. -URL: https://github.com/gabime/@PROJECT_NAME@ -Version: @SPDLOG_VERSION@ -CFlags: -I${includedir} @PKG_CONFIG_DEFINES@ -Libs: -L${libdir} -lspdlog -pthread -Requires: @PKG_CONFIG_REQUIRES@ - diff --git a/thirdparty/spdlog/cmake/spdlogCPack.cmake b/thirdparty/spdlog/cmake/spdlogCPack.cmake deleted file mode 100644 index 6ee2e51..0000000 --- a/thirdparty/spdlog/cmake/spdlogCPack.cmake +++ /dev/null @@ -1,32 +0,0 @@ -set(CPACK_GENERATOR - TGZ - ZIP - ) - -set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) -set(CPACK_INSTALL_CMAKE_PROJECTS - "${CMAKE_BINARY_DIR}" - "${PROJECT_NAME}" - ALL - . - ) - -set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog") -set(CPACK_PACKAGE_VENDOR "Gabi Melman") -set(CPACK_PACKAGE_CONTACT "Gabi Melman ") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fast C++ logging library") -set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) -set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) -if (PROJECT_VERSION_TWEAK) - set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK}) -endif () -set(CPACK_PACKAGE_RELOCATABLE ON) - -set(CPACK_RPM_PACKAGE_LICENSE "MIT") -set(CPACK_RPM_PACKAGE_GROUP "System Environment/Libraries") -set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL}) -set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") - -include(CPack) diff --git a/thirdparty/spdlog/cmake/spdlogConfig.cmake.in b/thirdparty/spdlog/cmake/spdlogConfig.cmake.in deleted file mode 100644 index 43ffcf7..0000000 --- a/thirdparty/spdlog/cmake/spdlogConfig.cmake.in +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright(c) 2019 spdlog authors -# Distributed under the MIT License (http://opensource.org/licenses/MIT) - -find_package(Threads REQUIRED) - -set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) -set(config_targets_file @config_targets_file@) - -if(SPDLOG_FMT_EXTERNAL) - include(CMakeFindDependencyMacro) - find_dependency(fmt CONFIG) -endif() - - -include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") diff --git a/thirdparty/spdlog/cmake/utils.cmake b/thirdparty/spdlog/cmake/utils.cmake deleted file mode 100644 index cfa1f45..0000000 --- a/thirdparty/spdlog/cmake/utils.cmake +++ /dev/null @@ -1,50 +0,0 @@ -# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION -function(spdlog_extract_version) - file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents) - string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}") - if(NOT CMAKE_MATCH_COUNT EQUAL 1) - message(FATAL_ERROR "Could not extract major version number from spdlog/version.h") - endif() - set(ver_major ${CMAKE_MATCH_1}) - - string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}") - if(NOT CMAKE_MATCH_COUNT EQUAL 1) - message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h") - endif() - - set(ver_minor ${CMAKE_MATCH_1}) - string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}") - if(NOT CMAKE_MATCH_COUNT EQUAL 1) - message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h") - endif() - set(ver_patch ${CMAKE_MATCH_1}) - - set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE) - set (SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE) -endfunction() - - -# Turn on warnings on the given target -function(spdlog_enable_warnings target_name) - target_compile_options(${target_name} PRIVATE - $<$,$,$>: - -Wall -Wextra -Wconversion -pedantic -Wfatal-errors> - $<$:/W4>) - if(MSVC_VERSION GREATER 1900) #Allow non fatal security wanrnings for msvc 2015 - target_compile_options(${target_name} PRIVATE /WX) - endif() -endfunction() - - -# Enable address sanitizer (gcc/clang only) -function(spdlog_enable_sanitizer target_name) - if (NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - message(FATAL_ERROR "Sanitizer supported only for gcc/clang") - endif() - message(STATUS "Address sanitizer enabled") - target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined) - target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow) - target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all) - target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) - target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold) -endfunction() diff --git a/thirdparty/spdlog/example/CMakeLists.txt b/thirdparty/spdlog/example/CMakeLists.txt deleted file mode 100644 index 458ca95..0000000 --- a/thirdparty/spdlog/example/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright(c) 2019 spdlog authors -# Distributed under the MIT License (http://opensource.org/licenses/MIT) - -cmake_minimum_required(VERSION 3.1) -project(spdlog_examples CXX) - -if(NOT TARGET spdlog) - # Stand-alone build - find_package(spdlog REQUIRED) -endif() - -#--------------------------------------------------------------------------------------- -# Example of using pre-compiled library -#--------------------------------------------------------------------------------------- -add_executable(example example.cpp) -spdlog_enable_warnings(example) -target_link_libraries(example PRIVATE spdlog::spdlog) - -#--------------------------------------------------------------------------------------- -# Example of using header-only library -#--------------------------------------------------------------------------------------- -if(SPDLOG_BUILD_EXAMPLE_HO) - add_executable(example_header_only example.cpp) - spdlog_enable_warnings(example_header_only) - target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only) -endif() - diff --git a/thirdparty/spdlog/example/example.cpp b/thirdparty/spdlog/example/example.cpp deleted file mode 100644 index 0bc838b..0000000 --- a/thirdparty/spdlog/example/example.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -// spdlog usage example - -#include - -void stdout_logger_example(); -void basic_example(); -void rotating_example(); -void daily_example(); -void async_example(); -void binary_example(); -void trace_example(); -void multi_sink_example(); -void user_defined_example(); -void err_handler_example(); -void syslog_example(); - -#include "spdlog/spdlog.h" - -int main(int, char *[]) -{ - spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); - spdlog::warn("Easy padding in numbers like {:08d}", 12); - spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); - spdlog::info("Support for floats {:03.2f}", 1.23456); - spdlog::info("Positional args are {1} {0}..", "too", "supported"); - spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left"); - - // Runtime log levels - spdlog::set_level(spdlog::level::info); // Set global log level to info - spdlog::debug("This message should not be displayed!"); - spdlog::set_level(spdlog::level::trace); // Set specific logger's log level - spdlog::debug("This message should be displayed.."); - - // Customize msg format for all loggers - spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); - spdlog::info("This an info message with custom format"); - spdlog::set_pattern("%+"); // back to default format - spdlog::set_level(spdlog::level::info); - - // Backtrace support - // Loggers can store in a ring buffer all messages (including debug/trace) for later inspection. - // When needed, call dump_backtrace() to see what happened: - spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages - for (int i = 0; i < 100; i++) - { - spdlog::debug("Backtrace message {}", i); // not logged.. - } - // e.g. if some error happened: - spdlog::dump_backtrace(); // log them now! - - try - { - stdout_logger_example(); - basic_example(); - rotating_example(); - daily_example(); - async_example(); - binary_example(); - multi_sink_example(); - user_defined_example(); - err_handler_example(); - trace_example(); - - // Flush all *registered* loggers using a worker thread every 3 seconds. - // note: registered loggers *must* be thread safe for this to work correctly! - spdlog::flush_every(std::chrono::seconds(3)); - - // Apply some function on all registered loggers - spdlog::apply_all([&](std::shared_ptr l) { l->info("End of example."); }); - - // Release all spdlog resources, and drop all loggers in the registry. - // This is optional (only mandatory if using windows + async log). - spdlog::shutdown(); - } - - // Exceptions will only be thrown upon failed logger or sink construction (not during logging). - catch (const spdlog::spdlog_ex &ex) - { - std::printf("Log initialization failed: %s\n", ex.what()); - return 1; - } -} - -#include "spdlog/sinks/stdout_color_sinks.h" -// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. -void stdout_logger_example() -{ - // Create color multi threaded logger. - auto console = spdlog::stdout_color_mt("console"); - // or for stderr: - // auto console = spdlog::stderr_color_mt("error-logger"); -} - -#include "spdlog/sinks/basic_file_sink.h" -void basic_example() -{ - // Create basic file logger (not rotated). - auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt"); -} - -#include "spdlog/sinks/rotating_file_sink.h" -void rotating_example() -{ - // Create a file rotating logger with 5mb size max and 3 rotated files. - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); -} - -#include "spdlog/sinks/daily_file_sink.h" -void daily_example() -{ - // Create a daily logger - a new file is created every day on 2:30am. - auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); -} - -#include "spdlog/async.h" -void async_example() -{ - // Default thread pool settings can be modified *before* creating the async logger: - // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread. - auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); - // alternatively: - // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); - - for (int i = 1; i < 101; ++i) - { - async_file->info("Async message #{}", i); - } -} - -// Log binary data as hex. -// Many types of std::container types can be used. -// Iterator ranges are supported too. -// Format flags: -// {:X} - print in uppercase. -// {:s} - don't separate each byte with space. -// {:p} - don't print the position on each line start. -// {:n} - don't split the output to lines. - -#include "spdlog/fmt/bin_to_hex.h" -void binary_example() -{ - std::vector buf(80); - for (int i = 0; i < 80; i++) - { - buf.push_back(static_cast(i & 0xff)); - } - spdlog::info("Binary example: {}", spdlog::to_hex(buf)); - spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); - // more examples: - // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); - // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); - // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); -} - -// Compile time log levels. -// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) -void trace_example() -{ - // trace from default logger - SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23); - // debug from default logger - SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23); - - // trace from logger object - auto logger = spdlog::get("file_logger"); - SPDLOG_LOGGER_TRACE(logger, "another trace message"); -} - -// A logger with multiple sinks (stdout and file) - each with a different format and log level. -void multi_sink_example() -{ - auto console_sink = std::make_shared(); - console_sink->set_level(spdlog::level::warn); - console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); - - auto file_sink = std::make_shared("logs/multisink.txt", true); - file_sink->set_level(spdlog::level::trace); - - spdlog::logger logger("multi_sink", {console_sink, file_sink}); - logger.set_level(spdlog::level::debug); - logger.warn("this should appear in both console and file"); - logger.info("this message should not appear in the console, only in the file"); -} - -// User defined types logging by implementing operator<< -#include "spdlog/fmt/ostr.h" // must be included -struct my_type -{ - int i; - template - friend OStream &operator<<(OStream &os, const my_type &c) - { - return os << "[my_type i=" << c.i << "]"; - } -}; - -void user_defined_example() -{ - spdlog::info("user defined type: {}", my_type{14}); -} - -// Custom error handler. Will be triggered on log failure. -void err_handler_example() -{ - // can be set globally or per logger(logger->set_error_handler(..)) - spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); }); -} - -// syslog example (linux/osx/freebsd) -#ifndef _WIN32 -#include "spdlog/sinks/syslog_sink.h" -void syslog_example() -{ - std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); - syslog_logger->warn("This is warning that will end up in syslog."); -} -#endif - -// Android example. -#if defined(__ANDROID__) -#include "spdlog/sinks/android_sink.h" -void android_example() -{ - std::string tag = "spdlog-android"; - auto android_logger = spdlog::android_logger_mt("android", tag); - android_logger->critical("Use \"adb shell logcat\" to view this message."); -} - -#endif diff --git a/thirdparty/spdlog/example/meson.build b/thirdparty/spdlog/example/meson.build deleted file mode 100644 index c37c4c3..0000000 --- a/thirdparty/spdlog/example/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -executable('example', 'example.cpp', dependencies: spdlog_dep) -executable('example_header_only', 'example.cpp', dependencies: spdlog_headeronly_dep) - - diff --git a/thirdparty/spdlog/include/spdlog/async.h b/thirdparty/spdlog/include/spdlog/async.h deleted file mode 100644 index afaf263..0000000 --- a/thirdparty/spdlog/include/spdlog/async.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// -// Async logging using global thread pool -// All loggers created here share same global thread pool. -// Each log message is pushed to a queue along with a shared pointer to the -// logger. -// If a logger deleted while having pending messages in the queue, it's actual -// destruction will defer -// until all its messages are processed by the thread pool. -// This is because each message in the queue holds a shared_ptr to the -// originating logger. - -#include -#include -#include - -#include -#include -#include - -namespace spdlog { - -namespace details { -static const size_t default_async_q_size = 8192; -} - -// async logger factory - creates async loggers backed with thread pool. -// if a global thread pool doesn't already exist, create it with default queue -// size of 8192 items and single thread. -template -struct async_factory_impl -{ - template - static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) - { - auto ®istry_inst = details::registry::instance(); - - // create global thread pool if not already exists.. - - auto &mutex = registry_inst.tp_mutex(); - std::lock_guard tp_lock(mutex); - auto tp = registry_inst.get_tp(); - if (tp == nullptr) - { - tp = std::make_shared(details::default_async_q_size, 1); - registry_inst.set_tp(tp); - } - - auto sink = std::make_shared(std::forward(args)...); - auto new_logger = std::make_shared(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy); - registry_inst.initialize_logger(new_logger); - return new_logger; - } -}; - -using async_factory = async_factory_impl; -using async_factory_nonblock = async_factory_impl; - -template -inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&... sink_args) -{ - return async_factory::create(std::move(logger_name), std::forward(sink_args)...); -} - -template -inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&... sink_args) -{ - return async_factory_nonblock::create(std::move(logger_name), std::forward(sink_args)...); -} - -// set global thread pool. -inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start) -{ - auto tp = std::make_shared(q_size, thread_count, on_thread_start); - details::registry::instance().set_tp(std::move(tp)); -} - -// set global thread pool. -inline void init_thread_pool(size_t q_size, size_t thread_count) -{ - init_thread_pool(q_size, thread_count, [] {}); -} - -// get the global thread pool. -inline std::shared_ptr thread_pool() -{ - return details::registry::instance().get_tp(); -} -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/async_logger-inl.h b/thirdparty/spdlog/include/spdlog/async_logger-inl.h deleted file mode 100644 index 356db0e..0000000 --- a/thirdparty/spdlog/include/spdlog/async_logger-inl.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include -#include - -#include -#include - -SPDLOG_INLINE spdlog::async_logger::async_logger( - std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy) - : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy) -{} - -SPDLOG_INLINE spdlog::async_logger::async_logger( - std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy) - : async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) -{} - -// send the log message to the thread pool -SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg) -{ - if (auto pool_ptr = thread_pool_.lock()) - { - pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); - } - else - { - SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore")); - } -} - -// send flush request to the thread pool -SPDLOG_INLINE void spdlog::async_logger::flush_() -{ - if (auto pool_ptr = thread_pool_.lock()) - { - pool_ptr->post_flush(shared_from_this(), overflow_policy_); - } - else - { - SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore")); - } -} - -// -// backend functions - called from the thread pool to do the actual job -// -SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) -{ - for (auto &sink : sinks_) - { - if (sink->should_log(msg.level)) - { - SPDLOG_TRY - { - sink->log(msg); - } - SPDLOG_LOGGER_CATCH() - } - } - - if (should_flush_(msg)) - { - backend_flush_(); - } -} - -SPDLOG_INLINE void spdlog::async_logger::backend_flush_() -{ - for (auto &sink : sinks_) - { - SPDLOG_TRY - { - sink->flush(); - } - SPDLOG_LOGGER_CATCH() - } -} - -SPDLOG_INLINE std::shared_ptr spdlog::async_logger::clone(std::string new_name) -{ - auto cloned = std::make_shared(*this); - cloned->name_ = std::move(new_name); - return cloned; -} diff --git a/thirdparty/spdlog/include/spdlog/async_logger.h b/thirdparty/spdlog/include/spdlog/async_logger.h deleted file mode 100644 index 829c5ac..0000000 --- a/thirdparty/spdlog/include/spdlog/async_logger.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// Fast asynchronous logger. -// Uses pre allocated queue. -// Creates a single back thread to pop messages from the queue and log them. -// -// Upon each log write the logger: -// 1. Checks if its log level is enough to log the message -// 2. Push a new copy of the message to a queue (or block the caller until -// space is available in the queue) -// Upon destruction, logs all remaining messages in the queue before -// destructing.. - -#include - -namespace spdlog { - -// Async overflow policy - block by default. -enum class async_overflow_policy -{ - block, // Block until message can be enqueued - overrun_oldest // Discard oldest message in the queue if full when trying to - // add new item. -}; - -namespace details { -class thread_pool; -} - -class async_logger final : public std::enable_shared_from_this, public logger -{ - friend class details::thread_pool; - -public: - template - async_logger(std::string logger_name, It begin, It end, std::weak_ptr tp, - async_overflow_policy overflow_policy = async_overflow_policy::block) - : logger(std::move(logger_name), begin, end) - , thread_pool_(std::move(tp)) - , overflow_policy_(overflow_policy) - {} - - async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, - async_overflow_policy overflow_policy = async_overflow_policy::block); - - async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, - async_overflow_policy overflow_policy = async_overflow_policy::block); - - std::shared_ptr clone(std::string new_name) override; - -protected: - void sink_it_(const details::log_msg &msg) override; - void flush_() override; - void backend_sink_it_(const details::log_msg &incoming_log_msg); - void backend_flush_(); - -private: - std::weak_ptr thread_pool_; - async_overflow_policy overflow_policy_; -}; -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "async_logger-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/common-inl.h b/thirdparty/spdlog/include/spdlog/common-inl.h deleted file mode 100644 index a21284d..0000000 --- a/thirdparty/spdlog/include/spdlog/common-inl.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -namespace spdlog { -namespace level { -static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; - -static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; - -SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT -{ - return level_string_views[l]; -} - -SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT -{ - return short_level_names[l]; -} - -SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT -{ - int level = 0; - for (const auto &level_str : level_string_views) - { - if (level_str == name) - { - return static_cast(level); - } - level++; - } - return level::off; -} -} // namespace level - -SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) - : msg_(std::move(msg)) -{} - -SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) -{ - memory_buf_t outbuf; - fmt::format_system_error(outbuf, last_errno, msg); - msg_ = fmt::to_string(outbuf); -} - -SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT -{ - return msg_.c_str(); -} - -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/common.h b/thirdparty/spdlog/include/spdlog/common.h deleted file mode 100644 index 830220e..0000000 --- a/thirdparty/spdlog/include/spdlog/common.h +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX // prevent windows redefining min/max -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include -#endif //_WIN32 - -#ifdef SPDLOG_COMPILED_LIB -#undef SPDLOG_HEADER_ONLY -#define SPDLOG_INLINE -#else -#define SPDLOG_HEADER_ONLY -#define SPDLOG_INLINE inline -#endif - -#include - -// visual studio upto 2013 does not support noexcept nor constexpr -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#define SPDLOG_NOEXCEPT _NOEXCEPT -#define SPDLOG_CONSTEXPR -#else -#define SPDLOG_NOEXCEPT noexcept -#define SPDLOG_CONSTEXPR constexpr -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define SPDLOG_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define SPDLOG_DEPRECATED __declspec(deprecated) -#else -#define SPDLOG_DEPRECATED -#endif - -// disable thread local on msvc 2013 -#ifndef SPDLOG_NO_TLS -#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) -#define SPDLOG_NO_TLS 1 -#endif -#endif - -#ifndef SPDLOG_FUNCTION -#define SPDLOG_FUNCTION static_cast(__FUNCTION__) -#endif - -#ifdef SPDLOG_NO_EXCEPTIONS -#define SPDLOG_TRY -#define SPDLOG_THROW(ex) \ - do \ - { \ - printf("spdlog fatal error: %s\n", ex.what()); \ - std::abort(); \ - } while (0) -#define SPDLOG_CATCH_ALL() -#else -#define SPDLOG_TRY try -#define SPDLOG_THROW(ex) throw(ex) -#define SPDLOG_CATCH_ALL() catch (...) -#endif - -namespace spdlog { - -class formatter; - -namespace sinks { -class sink; -} - -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -using filename_t = std::wstring; -#define SPDLOG_FILENAME_T(s) L##s -#else -using filename_t = std::string; -#define SPDLOG_FILENAME_T(s) s -#endif - -using log_clock = std::chrono::system_clock; -using sink_ptr = std::shared_ptr; -using sinks_init_list = std::initializer_list; -using err_handler = std::function; -using string_view_t = fmt::basic_string_view; -using wstring_view_t = fmt::basic_string_view; -using memory_buf_t = fmt::basic_memory_buffer; - -#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT -#ifndef _WIN32 -#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows -#else -template -struct is_convertible_to_wstring_view : std::is_convertible -{}; -#endif // _WIN32 -#else -template -struct is_convertible_to_wstring_view : std::false_type -{}; -#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT - -#if defined(SPDLOG_NO_ATOMIC_LEVELS) -using level_t = details::null_atomic_int; -#else -using level_t = std::atomic; -#endif - -#define SPDLOG_LEVEL_TRACE 0 -#define SPDLOG_LEVEL_DEBUG 1 -#define SPDLOG_LEVEL_INFO 2 -#define SPDLOG_LEVEL_WARN 3 -#define SPDLOG_LEVEL_ERROR 4 -#define SPDLOG_LEVEL_CRITICAL 5 -#define SPDLOG_LEVEL_OFF 6 - -#if !defined(SPDLOG_ACTIVE_LEVEL) -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO -#endif - -// Log level enum -namespace level { -enum level_enum -{ - trace = SPDLOG_LEVEL_TRACE, - debug = SPDLOG_LEVEL_DEBUG, - info = SPDLOG_LEVEL_INFO, - warn = SPDLOG_LEVEL_WARN, - err = SPDLOG_LEVEL_ERROR, - critical = SPDLOG_LEVEL_CRITICAL, - off = SPDLOG_LEVEL_OFF, -}; - -#if !defined(SPDLOG_LEVEL_NAMES) -#define SPDLOG_LEVEL_NAMES \ - { \ - "trace", "debug", "info", "warning", "error", "critical", "off" \ - } -#endif - -#if !defined(SPDLOG_SHORT_LEVEL_NAMES) - -#define SPDLOG_SHORT_LEVEL_NAMES \ - { \ - "T", "D", "I", "W", "E", "C", "O" \ - } -#endif - -string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; -const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; -spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT; - -using level_hasher = std::hash; -} // namespace level - -// -// Color mode used by sinks with color support. -// -enum class color_mode -{ - always, - automatic, - never -}; - -// -// Pattern time - specific time getting to use for pattern_formatter. -// local time by default -// -enum class pattern_time_type -{ - local, // log localtime - utc // log utc -}; - -// -// Log exception -// -class spdlog_ex : public std::exception -{ -public: - explicit spdlog_ex(std::string msg); - spdlog_ex(const std::string &msg, int last_errno); - const char *what() const SPDLOG_NOEXCEPT override; - -private: - std::string msg_; -}; - -struct source_loc -{ - SPDLOG_CONSTEXPR source_loc() = default; - SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in) - : filename{filename_in} - , line{line_in} - , funcname{funcname_in} - {} - - SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT - { - return line == 0; - } - const char *filename{nullptr}; - int line{0}; - const char *funcname{nullptr}; -}; - -namespace details { -// make_unique support for pre c++14 - -#if __cplusplus >= 201402L // C++14 and beyond -using std::make_unique; -#else -template -std::unique_ptr make_unique(Args &&... args) -{ - static_assert(!std::is_array::value, "arrays not supported"); - return std::unique_ptr(new T(std::forward(args)...)); -} -#endif -} // namespace details - -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "common-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/backtracer-inl.h b/thirdparty/spdlog/include/spdlog/details/backtracer-inl.h deleted file mode 100644 index 21553c2..0000000 --- a/thirdparty/spdlog/include/spdlog/details/backtracer-inl.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif -namespace spdlog { -namespace details { -SPDLOG_INLINE backtracer::backtracer(const backtracer &other) -{ - std::lock_guard lock(other.mutex_); - enabled_ = other.enabled(); - messages_ = other.messages_; -} - -SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT -{ - std::lock_guard lock(other.mutex_); - enabled_ = other.enabled(); - messages_ = std::move(other.messages_); -} - -SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) -{ - std::lock_guard lock(mutex_); - enabled_ = other.enabled(); - messages_ = std::move(other.messages_); - return *this; -} - -SPDLOG_INLINE void backtracer::enable(size_t size) -{ - std::lock_guard lock{mutex_}; - enabled_.store(true, std::memory_order_relaxed); - messages_ = circular_q{size}; -} - -SPDLOG_INLINE void backtracer::disable() -{ - std::lock_guard lock{mutex_}; - enabled_.store(false, std::memory_order_relaxed); -} - -SPDLOG_INLINE bool backtracer::enabled() const -{ - return enabled_.load(std::memory_order_relaxed); -} - -SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) -{ - std::lock_guard lock{mutex_}; - messages_.push_back(log_msg_buffer{msg}); -} - -// pop all items in the q and apply the given fun on each of them. -SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) -{ - std::lock_guard lock{mutex_}; - while (!messages_.empty()) - { - auto &front_msg = messages_.front(); - fun(front_msg); - messages_.pop_front(); - } -} -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/backtracer.h b/thirdparty/spdlog/include/spdlog/details/backtracer.h deleted file mode 100644 index 0e779ca..0000000 --- a/thirdparty/spdlog/include/spdlog/details/backtracer.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include - -#include -#include -#include - -// Store log messages in circular buffer. -// Useful for storing debug data in case of error/warning happens. - -namespace spdlog { -namespace details { -class backtracer -{ - mutable std::mutex mutex_; - std::atomic enabled_{false}; - circular_q messages_; - -public: - backtracer() = default; - backtracer(const backtracer &other); - - backtracer(backtracer &&other) SPDLOG_NOEXCEPT; - backtracer &operator=(backtracer other); - - void enable(size_t size); - void disable(); - bool enabled() const; - void push_back(const log_msg &msg); - - // pop all items in the q and apply the given fun on each of them. - void foreach_pop(std::function fun); -}; - -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "backtracer-inl.h" -#endif \ No newline at end of file diff --git a/thirdparty/spdlog/include/spdlog/details/circular_q.h b/thirdparty/spdlog/include/spdlog/details/circular_q.h deleted file mode 100644 index 1f2712e..0000000 --- a/thirdparty/spdlog/include/spdlog/details/circular_q.h +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -// circular q view of std::vector. -#pragma once - -#include -#include - -namespace spdlog { -namespace details { -template -class circular_q -{ - size_t max_items_ = 0; - typename std::vector::size_type head_ = 0; - typename std::vector::size_type tail_ = 0; - size_t overrun_counter_ = 0; - std::vector v_; - -public: - using value_type = T; - - // empty ctor - create a disabled queue with no elements allocated at all - circular_q() = default; - - explicit circular_q(size_t max_items) - : max_items_(max_items + 1) // one item is reserved as marker for full q - , v_(max_items_) - {} - - circular_q(const circular_q &) = default; - circular_q &operator=(const circular_q &) = default; - - // move cannot be default, - // since we need to reset head_, tail_, etc to zero in the moved object - circular_q(circular_q &&other) SPDLOG_NOEXCEPT - { - copy_moveable(std::move(other)); - } - - circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT - { - copy_moveable(std::move(other)); - return *this; - } - - // push back, overrun (oldest) item if no room left - void push_back(T &&item) - { - if (max_items_ > 0) - { - v_[tail_] = std::move(item); - tail_ = (tail_ + 1) % max_items_; - - if (tail_ == head_) // overrun last item if full - { - head_ = (head_ + 1) % max_items_; - ++overrun_counter_; - } - } - } - - // Return reference to the front item. - // If there are no elements in the container, the behavior is undefined. - const T &front() const - { - return v_[head_]; - } - - T &front() - { - return v_[head_]; - } - - // Return number of elements actually stored - size_t size() const - { - if (tail_ >= head_) - { - return tail_ - head_; - } - else - { - return max_items_ - (head_ - tail_); - } - } - - // Return const reference to item by index. - // If index is out of range 0…size()-1, the behavior is undefined. - const T &at(size_t i) const - { - assert(i < size()); - return v_[(head_ + i) % max_items_]; - } - - // Pop item from front. - // If there are no elements in the container, the behavior is undefined. - void pop_front() - { - head_ = (head_ + 1) % max_items_; - } - - bool empty() const - { - return tail_ == head_; - } - - bool full() const - { - // head is ahead of the tail by 1 - if (max_items_ > 0) - { - return ((tail_ + 1) % max_items_) == head_; - } - return false; - } - - size_t overrun_counter() const - { - return overrun_counter_; - } - -private: - // copy from other&& and reset it to disabled state - void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT - { - max_items_ = other.max_items_; - head_ = other.head_; - tail_ = other.tail_; - overrun_counter_ = other.overrun_counter_; - v_ = std::move(other.v_); - - // put &&other in disabled, but valid state - other.max_items_ = 0; - other.head_ = other.tail_ = 0; - other.overrun_counter_ = 0; - } -}; -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/console_globals.h b/thirdparty/spdlog/include/spdlog/details/console_globals.h deleted file mode 100644 index 665201d..0000000 --- a/thirdparty/spdlog/include/spdlog/details/console_globals.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include - -namespace spdlog { -namespace details { - -struct console_mutex -{ - using mutex_t = std::mutex; - static mutex_t &mutex() - { - static mutex_t s_mutex; - return s_mutex; - } -}; - -struct console_nullmutex -{ - using mutex_t = null_mutex; - static mutex_t &mutex() - { - static mutex_t s_mutex; - return s_mutex; - } -}; -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/file_helper-inl.h b/thirdparty/spdlog/include/spdlog/details/file_helper-inl.h deleted file mode 100644 index cdd45f1..0000000 --- a/thirdparty/spdlog/include/spdlog/details/file_helper-inl.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace spdlog { -namespace details { - -SPDLOG_INLINE file_helper::~file_helper() -{ - close(); -} - -SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) -{ - close(); - filename_ = fname; - auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); - - for (int tries = 0; tries < open_tries_; ++tries) - { - // create containing folder if not exists already. - os::create_dir(os::dir_name(fname)); - if (!os::fopen_s(&fd_, fname, mode)) - { - return; - } - - details::os::sleep_for_millis(open_interval_); - } - - SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno)); -} - -SPDLOG_INLINE void file_helper::reopen(bool truncate) -{ - if (filename_.empty()) - { - SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before")); - } - this->open(filename_, truncate); -} - -SPDLOG_INLINE void file_helper::flush() -{ - std::fflush(fd_); -} - -SPDLOG_INLINE void file_helper::close() -{ - if (fd_ != nullptr) - { - std::fclose(fd_); - fd_ = nullptr; - } -} - -SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) -{ - size_t msg_size = buf.size(); - auto data = buf.data(); - if (std::fwrite(data, 1, msg_size, fd_) != msg_size) - { - SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno)); - } -} - -SPDLOG_INLINE size_t file_helper::size() const -{ - if (fd_ == nullptr) - { - SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_))); - } - return os::filesize(fd_); -} - -SPDLOG_INLINE const filename_t &file_helper::filename() const -{ - return filename_; -} - -// -// return file path and its extension: -// -// "mylog.txt" => ("mylog", ".txt") -// "mylog" => ("mylog", "") -// "mylog." => ("mylog.", "") -// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") -// -// the starting dot in filenames is ignored (hidden files): -// -// ".mylog" => (".mylog". "") -// "my_folder/.mylog" => ("my_folder/.mylog", "") -// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") -SPDLOG_INLINE std::tuple file_helper::split_by_extension(const filename_t &fname) -{ - auto ext_index = fname.rfind('.'); - - // no valid extension found - return whole path and empty string as - // extension - if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) - { - return std::make_tuple(fname, filename_t()); - } - - // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" - auto folder_index = fname.rfind(details::os::folder_sep); - if (folder_index != filename_t::npos && folder_index >= ext_index - 1) - { - return std::make_tuple(fname, filename_t()); - } - - // finally - return a valid base and extension tuple - return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); -} - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/file_helper.h b/thirdparty/spdlog/include/spdlog/details/file_helper.h deleted file mode 100644 index 3228ce8..0000000 --- a/thirdparty/spdlog/include/spdlog/details/file_helper.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include - -namespace spdlog { -namespace details { - -// Helper class for file sinks. -// When failing to open a file, retry several times(5) with a delay interval(10 ms). -// Throw spdlog_ex exception on errors. - -class file_helper -{ -public: - explicit file_helper() = default; - - file_helper(const file_helper &) = delete; - file_helper &operator=(const file_helper &) = delete; - ~file_helper(); - - void open(const filename_t &fname, bool truncate = false); - void reopen(bool truncate); - void flush(); - void close(); - void write(const memory_buf_t &buf); - size_t size() const; - const filename_t &filename() const; - - // - // return file path and its extension: - // - // "mylog.txt" => ("mylog", ".txt") - // "mylog" => ("mylog", "") - // "mylog." => ("mylog.", "") - // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") - // - // the starting dot in filenames is ignored (hidden files): - // - // ".mylog" => (".mylog". "") - // "my_folder/.mylog" => ("my_folder/.mylog", "") - // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") - static std::tuple split_by_extension(const filename_t &fname); - -private: - const int open_tries_ = 5; - const int open_interval_ = 10; - std::FILE *fd_{nullptr}; - filename_t filename_; -}; -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "file_helper-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/fmt_helper.h b/thirdparty/spdlog/include/spdlog/details/fmt_helper.h deleted file mode 100644 index 85b988e..0000000 --- a/thirdparty/spdlog/include/spdlog/details/fmt_helper.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -#pragma once - -#include -#include -#include -#include - -// Some fmt helpers to efficiently format and pad ints and strings -namespace spdlog { -namespace details { -namespace fmt_helper { - -inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT -{ - return spdlog::string_view_t{buf.data(), buf.size()}; -} - -inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) -{ - auto *buf_ptr = view.data(); - if (buf_ptr != nullptr) - { - dest.append(buf_ptr, buf_ptr + view.size()); - } -} - -template -inline void append_int(T n, memory_buf_t &dest) -{ - fmt::format_int i(n); - dest.append(i.data(), i.data() + i.size()); -} - -template -inline unsigned count_digits(T n) -{ - using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type; - return static_cast(fmt::internal::count_digits(static_cast(n))); -} - -inline void pad2(int n, memory_buf_t &dest) -{ - if (n > 99) - { - append_int(n, dest); - } - else if (n > 9) // 10-99 - { - dest.push_back(static_cast('0' + n / 10)); - dest.push_back(static_cast('0' + n % 10)); - } - else if (n >= 0) // 0-9 - { - dest.push_back('0'); - dest.push_back(static_cast('0' + n)); - } - else // negatives (unlikely, but just in case, let fmt deal with it) - { - fmt::format_to(dest, "{:02}", n); - } -} - -template -inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) -{ - static_assert(std::is_unsigned::value, "pad_uint must get unsigned T"); - auto digits = count_digits(n); - if (width > digits) - { - const char *zeroes = "0000000000000000000"; - dest.append(zeroes, zeroes + width - digits); - } - append_int(n, dest); -} - -template -inline void pad3(T n, memory_buf_t &dest) -{ - pad_uint(n, 3, dest); -} - -template -inline void pad6(T n, memory_buf_t &dest) -{ - pad_uint(n, 6, dest); -} - -template -inline void pad9(T n, memory_buf_t &dest) -{ - pad_uint(n, 9, dest); -} - -// return fraction of a second of the given time_point. -// e.g. -// fraction(tp) -> will return the millis part of the second -template -inline ToDuration time_fraction(log_clock::time_point tp) -{ - using std::chrono::duration_cast; - using std::chrono::seconds; - auto duration = tp.time_since_epoch(); - auto secs = duration_cast(duration); - return duration_cast(duration) - duration_cast(secs); -} - -} // namespace fmt_helper -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/log_msg-inl.h b/thirdparty/spdlog/include/spdlog/details/log_msg-inl.h deleted file mode 100644 index 0a0aed8..0000000 --- a/thirdparty/spdlog/include/spdlog/details/log_msg-inl.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include - -namespace spdlog { -namespace details { - -SPDLOG_INLINE log_msg::log_msg( - spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg) - : logger_name(a_logger_name) - , level(lvl) - , time(os::now()) -#ifndef SPDLOG_NO_THREAD_ID - , thread_id(os::thread_id()) -#endif - , source(loc) - , payload(msg) -{} - -SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg) - : log_msg(source_loc{}, a_logger_name, lvl, msg) -{} - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/log_msg.h b/thirdparty/spdlog/include/spdlog/details/log_msg.h deleted file mode 100644 index 9ae473d..0000000 --- a/thirdparty/spdlog/include/spdlog/details/log_msg.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include - -namespace spdlog { -namespace details { -struct log_msg -{ - log_msg() = default; - log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg); - log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg); - log_msg(const log_msg &other) = default; - - string_view_t logger_name; - level::level_enum level{level::off}; - log_clock::time_point time; - size_t thread_id{0}; - - // wrapping the formatted text with color (updated by pattern_formatter). - mutable size_t color_range_start{0}; - mutable size_t color_range_end{0}; - - source_loc source; - string_view_t payload; -}; -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "log_msg-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/log_msg_buffer-inl.h b/thirdparty/spdlog/include/spdlog/details/log_msg_buffer-inl.h deleted file mode 100644 index 51f4d5b..0000000 --- a/thirdparty/spdlog/include/spdlog/details/log_msg_buffer-inl.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -namespace spdlog { -namespace details { - -SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) - : log_msg{orig_msg} -{ - buffer.append(logger_name.begin(), logger_name.end()); - buffer.append(payload.begin(), payload.end()); - update_string_views(); -} - -SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) - : log_msg{other} -{ - buffer.append(logger_name.begin(), logger_name.end()); - buffer.append(payload.begin(), payload.end()); - update_string_views(); -} - -SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)} -{ - update_string_views(); -} - -SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) -{ - log_msg::operator=(other); - buffer.clear(); - buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size()); - update_string_views(); - return *this; -} - -SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT -{ - log_msg::operator=(other); - buffer = std::move(other.buffer); - update_string_views(); - return *this; -} - -SPDLOG_INLINE void log_msg_buffer::update_string_views() -{ - logger_name = string_view_t{buffer.data(), logger_name.size()}; - payload = string_view_t{buffer.data() + logger_name.size(), payload.size()}; -} - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/log_msg_buffer.h b/thirdparty/spdlog/include/spdlog/details/log_msg_buffer.h deleted file mode 100644 index c20ae7b..0000000 --- a/thirdparty/spdlog/include/spdlog/details/log_msg_buffer.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include - -namespace spdlog { -namespace details { - -// Extend log_msg with internal buffer to store its payload. -// THis is needed since log_msg holds string_views that points to stack data. - -class log_msg_buffer : public log_msg -{ - memory_buf_t buffer; - void update_string_views(); - -public: - log_msg_buffer() = default; - explicit log_msg_buffer(const log_msg &orig_msg); - log_msg_buffer(const log_msg_buffer &other); - log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT; - log_msg_buffer &operator=(const log_msg_buffer &other); - log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT; -}; - -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "log_msg_buffer-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/mpmc_blocking_q.h b/thirdparty/spdlog/include/spdlog/details/mpmc_blocking_q.h deleted file mode 100644 index 7f8a253..0000000 --- a/thirdparty/spdlog/include/spdlog/details/mpmc_blocking_q.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// multi producer-multi consumer blocking queue. -// enqueue(..) - will block until room found to put the new message. -// enqueue_nowait(..) - will return immediately with false if no room left in -// the queue. -// dequeue_for(..) - will block until the queue is not empty or timeout have -// passed. - -#include - -#include -#include - -namespace spdlog { -namespace details { - -template -class mpmc_blocking_queue -{ -public: - using item_type = T; - explicit mpmc_blocking_queue(size_t max_items) - : q_(max_items) - {} - -#ifndef __MINGW32__ - // try to enqueue and block if no room left - void enqueue(T &&item) - { - { - std::unique_lock lock(queue_mutex_); - pop_cv_.wait(lock, [this] { return !this->q_.full(); }); - q_.push_back(std::move(item)); - } - push_cv_.notify_one(); - } - - // enqueue immediately. overrun oldest message in the queue if no room left. - void enqueue_nowait(T &&item) - { - { - std::unique_lock lock(queue_mutex_); - q_.push_back(std::move(item)); - } - push_cv_.notify_one(); - } - - // try to dequeue item. if no item found. wait upto timeout and try again - // Return true, if succeeded dequeue item, false otherwise - bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) - { - { - std::unique_lock lock(queue_mutex_); - if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) - { - return false; - } - popped_item = std::move(q_.front()); - q_.pop_front(); - } - pop_cv_.notify_one(); - return true; - } - -#else - // apparently mingw deadlocks if the mutex is released before cv.notify_one(), - // so release the mutex at the very end each function. - - // try to enqueue and block if no room left - void enqueue(T &&item) - { - std::unique_lock lock(queue_mutex_); - pop_cv_.wait(lock, [this] { return !this->q_.full(); }); - q_.push_back(std::move(item)); - push_cv_.notify_one(); - } - - // enqueue immediately. overrun oldest message in the queue if no room left. - void enqueue_nowait(T &&item) - { - std::unique_lock lock(queue_mutex_); - q_.push_back(std::move(item)); - push_cv_.notify_one(); - } - - // try to dequeue item. if no item found. wait upto timeout and try again - // Return true, if succeeded dequeue item, false otherwise - bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) - { - std::unique_lock lock(queue_mutex_); - if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) - { - return false; - } - popped_item = std::move(q_.front()); - q_.pop_front(); - pop_cv_.notify_one(); - return true; - } - -#endif - - size_t overrun_counter() - { - std::unique_lock lock(queue_mutex_); - return q_.overrun_counter(); - } - -private: - std::mutex queue_mutex_; - std::condition_variable push_cv_; - std::condition_variable pop_cv_; - spdlog::details::circular_q q_; -}; -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/null_mutex.h b/thirdparty/spdlog/include/spdlog/details/null_mutex.h deleted file mode 100644 index 83533d4..0000000 --- a/thirdparty/spdlog/include/spdlog/details/null_mutex.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include -// null, no cost dummy "mutex" and dummy "atomic" int - -namespace spdlog { -namespace details { -struct null_mutex -{ - void lock() const {} - void unlock() const {} - bool try_lock() const - { - return true; - } -}; - -struct null_atomic_int -{ - int value; - null_atomic_int() = default; - - explicit null_atomic_int(int new_value) - : value(new_value) - {} - - int load(std::memory_order = std::memory_order_relaxed) const - { - return value; - } - - void store(int new_value, std::memory_order = std::memory_order_relaxed) - { - value = new_value; - } - - int exchange(int new_value, std::memory_order = std::memory_order_relaxed) - { - std::swap(new_value, value); - return new_value; // return value before the call - } -}; - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/os-inl.h b/thirdparty/spdlog/include/spdlog/details/os-inl.h deleted file mode 100644 index 8473eb0..0000000 --- a/thirdparty/spdlog/include/spdlog/details/os-inl.h +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 - -#ifndef NOMINMAX -#define NOMINMAX // prevent windows redefining min/max -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include // _get_osfhandle and _isatty support -#include // _get_pid support -#include - -#ifdef __MINGW32__ -#include -#endif - -#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES) -#include -#endif - -#include // for _mkdir/_wmkdir - -#else // unix - -#include -#include - -#ifdef __linux__ -#include //Use gettid() syscall under linux to get thread id - -#elif defined(_AIX) -#include // for pthread_getthreadid_np - -#elif defined(__DragonFly__) || defined(__FreeBSD__) -#include // for pthread_getthreadid_np - -#elif defined(__NetBSD__) -#include // for _lwp_self - -#elif defined(__sun) -#include // for thr_self -#endif - -#endif // unix - -#ifndef __has_feature // Clang - feature checking macros. -#define __has_feature(x) 0 // Compatibility with non-clang compilers. -#endif - -namespace spdlog { -namespace details { -namespace os { - -SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT -{ - -#if defined __linux__ && defined SPDLOG_CLOCK_COARSE - timespec ts; - ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); - return std::chrono::time_point( - std::chrono::duration_cast(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); - -#else - return log_clock::now(); -#endif -} -SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - std::tm tm; - ::localtime_s(&tm, &time_tt); -#else - std::tm tm; - ::localtime_r(&time_tt, &tm); -#endif - return tm; -} - -SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT -{ - std::time_t now_t = ::time(nullptr); - return localtime(now_t); -} - -SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - std::tm tm; - ::gmtime_s(&tm, &time_tt); -#else - std::tm tm; - ::gmtime_r(&time_tt, &tm); -#endif - return tm; -} - -SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT -{ - std::time_t now_t = ::time(nullptr); - return gmtime(now_t); -} - -#ifdef SPDLOG_PREVENT_CHILD_FD -SPDLOG_INLINE void prevent_child_fd(FILE *f) -{ -#ifdef _WIN32 - auto file_handle = reinterpret_cast(_get_osfhandle(::_fileno(f))); - if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) - SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno)); -#else - auto fd = ::fileno(f); - if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno)); - } -#endif -} -#endif // SPDLOG_PREVENT_CHILD_FD - -// fopen_s on non windows for writing -SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#else - *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#endif -#else // unix - *fp = ::fopen((filename.c_str()), mode.c_str()); -#endif - -#ifdef SPDLOG_PREVENT_CHILD_FD - // prevent child processes from inheriting log file descriptors - if (*fp != nullptr) - { - prevent_child_fd(*fp); - } -#endif - return *fp == nullptr; -} - -SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return ::_wremove(filename.c_str()); -#else - return std::remove(filename.c_str()); -#endif -} - -SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT -{ - return path_exists(filename) ? remove(filename) : 0; -} - -SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return ::_wrename(filename1.c_str(), filename2.c_str()); -#else - return std::rename(filename1.c_str(), filename2.c_str()); -#endif -} - -// Return true if path exists (file or directory) -SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - auto attribs = ::GetFileAttributesW(filename.c_str()); -#else - auto attribs = ::GetFileAttributesA(filename.c_str()); -#endif - return attribs != INVALID_FILE_ATTRIBUTES; -#else // common linux/unix all have the stat system call - struct stat buffer; - return (::stat(filename.c_str(), &buffer) == 0); -#endif -} - -// Return file size according to open FILE* object -SPDLOG_INLINE size_t filesize(FILE *f) -{ - if (f == nullptr) - { - SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null")); - } -#if defined(_WIN32) && !defined(__CYGWIN__) - int fd = ::_fileno(f); -#if _WIN64 // 64 bits - __int64 ret = ::_filelengthi64(fd); - if (ret >= 0) - { - return static_cast(ret); - } - -#else // windows 32 bits - long ret = ::_filelength(fd); - if (ret >= 0) - { - return static_cast(ret); - } -#endif - -#else // unix -// OpenBSD doesn't compile with :: before the fileno(..) -#if defined(__OpenBSD__) - int fd = fileno(f); -#else - int fd = ::fileno(f); -#endif -// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) -#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) - struct stat64 st; - if (::fstat64(fd, &st) == 0) - { - return static_cast(st.st_size); - } -#else // other unix or linux 32 bits or cygwin - struct stat st; - if (::fstat(fd, &st) == 0) - { - return static_cast(st.st_size); - } -#endif -#endif - SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno)); -} - -// Return utc offset in minutes or throw spdlog_ex on failure -SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) -{ - -#ifdef _WIN32 -#if _WIN32_WINNT < _WIN32_WINNT_WS08 - TIME_ZONE_INFORMATION tzinfo; - auto rv = ::GetTimeZoneInformation(&tzinfo); -#else - DYNAMIC_TIME_ZONE_INFORMATION tzinfo; - auto rv = ::GetDynamicTimeZoneInformation(&tzinfo); -#endif - if (rv == TIME_ZONE_ID_INVALID) - SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno)); - - int offset = -tzinfo.Bias; - if (tm.tm_isdst) - { - offset -= tzinfo.DaylightBias; - } - else - { - offset -= tzinfo.StandardBias; - } - return offset; -#else - -#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) - // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris - struct helper - { - static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime()) - { - int local_year = localtm.tm_year + (1900 - 1); - int gmt_year = gmtm.tm_year + (1900 - 1); - - long int days = ( - // difference in day of year - localtm.tm_yday - - gmtm.tm_yday - - // + intervening leap days - + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) + - ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) - - // + difference in years * 365 */ - + (long int)(local_year - gmt_year) * 365); - - long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); - long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); - long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); - - return secs; - } - }; - - auto offset_seconds = helper::calculate_gmt_offset(tm); -#else - auto offset_seconds = tm.tm_gmtoff; -#endif - - return static_cast(offset_seconds / 60); -#endif -} - -// Return current thread id as size_t -// It exists because the std::this_thread::get_id() is much slower(especially -// under VS 2013) -SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 - return static_cast(::GetCurrentThreadId()); -#elif defined(__linux__) -#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) -#define SYS_gettid __NR_gettid -#endif - return static_cast(::syscall(SYS_gettid)); -#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__) - return static_cast(::pthread_getthreadid_np()); -#elif defined(__NetBSD__) - return static_cast(::_lwp_self()); -#elif defined(__OpenBSD__) - return static_cast(::getthrid()); -#elif defined(__sun) - return static_cast(::thr_self()); -#elif __APPLE__ - uint64_t tid; - pthread_threadid_np(nullptr, &tid); - return static_cast(tid); -#else // Default to standard C++11 (other Unix) - return static_cast(std::hash()(std::this_thread::get_id())); -#endif -} - -// Return current thread id as size_t (from thread local storage) -SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT -{ -#if defined(SPDLOG_NO_TLS) - return _thread_id(); -#else // cache thread id in tls - static thread_local const size_t tid = _thread_id(); - return tid; -#endif -} - -// This is avoid msvc issue in sleep_for that happens if the clock changes. -// See https://github.com/gabime/spdlog/issues/609 -SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) - ::Sleep(milliseconds); -#else - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); -#endif -} - -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) -{ - memory_buf_t buf; - wstr_to_utf8buf(filename, buf); - return fmt::to_string(buf); -} -#else -SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) -{ - return filename; -} -#endif - -SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - return static_cast(::GetCurrentProcessId()); -#else - return static_cast(::getpid()); -#endif -} - -// Determine if the terminal supports colors -// Source: https://github.com/agauniyal/rang/ -SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 - return true; -#else - static constexpr std::array Terms = { - {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}}; - - const char *env_p = std::getenv("TERM"); - if (env_p == nullptr) - { - return false; - } - - static const bool result = - std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); - return result; -#endif -} - -// Detrmine if the terminal attached -// Source: https://github.com/agauniyal/rang/ -SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - return ::_isatty(_fileno(file)) != 0; -#else - return ::isatty(fileno(file)) != 0; -#endif -} - -#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) -SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) -{ - if (wstr.size() > static_cast((std::numeric_limits::max)())) - { - SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8")); - } - - int wstr_size = static_cast(wstr.size()); - if (wstr_size == 0) - { - target.resize(0); - return; - } - - int result_size = static_cast(target.capacity()); - if ((wstr_size + 1) * 2 > result_size) - { - result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL); - } - - if (result_size > 0) - { - target.resize(result_size); - result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL); - - if (result_size > 0) - { - target.resize(result_size); - return; - } - } - - SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()))); -} -#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) - -// return true on success -static SPDLOG_INLINE bool mkdir_(const filename_t &path) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - return ::_wmkdir(path.c_str()) == 0; -#else - return ::_mkdir(path.c_str()) == 0; -#endif -#else - return ::mkdir(path.c_str(), mode_t(0755)) == 0; -#endif -} - -// create the given directory - and all directories leading to it -// return true on success or if the directory already exists -SPDLOG_INLINE bool create_dir(filename_t path) -{ - if (path_exists(path)) - { - return true; - } - - if (path.empty()) - { - return false; - } - -#ifdef _WIN32 - // support forward slash in windows - std::replace(path.begin(), path.end(), '/', folder_sep); -#endif - - size_t search_offset = 0; - do - { - auto token_pos = path.find(folder_sep, search_offset); - // treat the entire path as a folder if no folder separator not found - if (token_pos == filename_t::npos) - { - token_pos = path.size(); - } - - auto subdir = path.substr(0, token_pos); - - if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) - { - return false; // return error if failed creating dir - } - search_offset = token_pos + 1; - } while (search_offset < path.size()); - - return true; -} - -// Return directory name from given path or empty string -// "abc/file" => "abc" -// "abc/" => "abc" -// "abc" => "" -// "abc///" => "abc//" -SPDLOG_INLINE filename_t dir_name(filename_t path) -{ -#ifdef _WIN32 - // support forward slash in windows - std::replace(path.begin(), path.end(), '/', folder_sep); -#endif - auto pos = path.find_last_of(folder_sep); - return pos != filename_t::npos ? path.substr(0, pos) : filename_t{}; -} - -} // namespace os -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/os.h b/thirdparty/spdlog/include/spdlog/details/os.h deleted file mode 100644 index 0894a6c..0000000 --- a/thirdparty/spdlog/include/spdlog/details/os.h +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include // std::time_t - -namespace spdlog { -namespace details { -namespace os { - -spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT; - -std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; - -std::tm localtime() SPDLOG_NOEXCEPT; - -std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; - -std::tm gmtime() SPDLOG_NOEXCEPT; - -// eol definition -#if !defined(SPDLOG_EOL) -#ifdef _WIN32 -#define SPDLOG_EOL "\r\n" -#else -#define SPDLOG_EOL "\n" -#endif -#endif - -SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; - -// folder separator -#ifdef _WIN32 -static const char folder_sep = '\\'; -#else -SPDLOG_CONSTEXPR static const char folder_sep = '/'; -#endif - -#ifdef SPDLOG_PREVENT_CHILD_FD -void prevent_child_fd(FILE *f); -#endif - -// fopen_s on non windows for writing -bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode); - -// Remove filename. return 0 on success -int remove(const filename_t &filename) SPDLOG_NOEXCEPT; - -// Remove file if exists. return 0 on success -// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread) -int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT; - -int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT; - -// Return if file exists. -bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT; - -// Return file size according to open FILE* object -size_t filesize(FILE *f); - -// Return utc offset in minutes or throw spdlog_ex on failure -int utc_minutes_offset(const std::tm &tm = details::os::localtime()); - -// Return current thread id as size_t -// It exists because the std::this_thread::get_id() is much slower(especially -// under VS 2013) -size_t _thread_id() SPDLOG_NOEXCEPT; - -// Return current thread id as size_t (from thread local storage) -size_t thread_id() SPDLOG_NOEXCEPT; - -// This is avoid msvc issue in sleep_for that happens if the clock changes. -// See https://github.com/gabime/spdlog/issues/609 -void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT; - -std::string filename_to_str(const filename_t &filename); - -int pid() SPDLOG_NOEXCEPT; - -// Determine if the terminal supports colors -// Source: https://github.com/agauniyal/rang/ -bool is_color_terminal() SPDLOG_NOEXCEPT; - -// Determine if the terminal attached -// Source: https://github.com/agauniyal/rang/ -bool in_terminal(FILE *file) SPDLOG_NOEXCEPT; - -#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) -void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target); -#endif - -// Return directory name from given path or empty string -// "abc/file" => "abc" -// "abc/" => "abc" -// "abc" => "" -// "abc///" => "abc//" -filename_t dir_name(filename_t path); - -// Create a dir from the given path. -// Return true if succeeded or if this dir already exists. -bool create_dir(filename_t path); - -} // namespace os -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "os-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/pattern_formatter-inl.h b/thirdparty/spdlog/include/spdlog/details/pattern_formatter-inl.h deleted file mode 100644 index 6fdc78a..0000000 --- a/thirdparty/spdlog/include/spdlog/details/pattern_formatter-inl.h +++ /dev/null @@ -1,1317 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog { -namespace details { - -/////////////////////////////////////////////////////////////////////// -// name & level pattern appender -/////////////////////////////////////////////////////////////////////// - -class scoped_padder -{ -public: - scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest) - : padinfo_(padinfo) - , dest_(dest) - { - remaining_pad_ = static_cast(padinfo.width_) - static_cast(wrapped_size); - if (remaining_pad_ <= 0) - { - return; - } - - if (padinfo_.side_ == padding_info::left) - { - pad_it(remaining_pad_); - remaining_pad_ = 0; - } - else if (padinfo_.side_ == padding_info::center) - { - auto half_pad = remaining_pad_ / 2; - auto reminder = remaining_pad_ & 1; - pad_it(half_pad); - remaining_pad_ = half_pad + reminder; // for the right side - } - } - - ~scoped_padder() - { - if (remaining_pad_ >= 0) - { - pad_it(remaining_pad_); - } - else if (padinfo_.truncate_) - { - long new_size = static_cast(dest_.size()) + remaining_pad_; - dest_.resize(static_cast(new_size)); - } - } - -private: - void pad_it(long count) - { - fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast(count)), dest_); - } - - const padding_info &padinfo_; - memory_buf_t &dest_; - long remaining_pad_; - string_view_t spaces_{" ", 64}; -}; - -struct null_scoped_padder -{ - null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {} -}; - -template -class name_formatter : public flag_formatter -{ -public: - explicit name_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - ScopedPadder p(msg.logger_name.size(), padinfo_, dest); - fmt_helper::append_string_view(msg.logger_name, dest); - } -}; - -// log level appender -template -class level_formatter : public flag_formatter -{ -public: - explicit level_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - string_view_t &level_name = level::to_string_view(msg.level); - ScopedPadder p(level_name.size(), padinfo_, dest); - fmt_helper::append_string_view(level_name, dest); - } -}; - -// short log level appender -template -class short_level_formatter : public flag_formatter -{ -public: - explicit short_level_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - string_view_t level_name{level::to_short_c_str(msg.level)}; - ScopedPadder p(level_name.size(), padinfo_, dest); - fmt_helper::append_string_view(level_name, dest); - } -}; - -/////////////////////////////////////////////////////////////////////// -// Date time pattern appenders -/////////////////////////////////////////////////////////////////////// - -static const char *ampm(const tm &t) -{ - return t.tm_hour >= 12 ? "PM" : "AM"; -} - -static int to12h(const tm &t) -{ - return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; -} - -// Abbreviated weekday name -static std::array days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}}; - -template -class a_formatter : public flag_formatter -{ -public: - explicit a_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - string_view_t field_value{days[static_cast(tm_time.tm_wday)]}; - ScopedPadder p(field_value.size(), padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Full weekday name -static std::array full_days{{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}}; - -template -class A_formatter : public flag_formatter -{ -public: - explicit A_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - string_view_t field_value{full_days[static_cast(tm_time.tm_wday)]}; - ScopedPadder p(field_value.size(), padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Abbreviated month -static const std::array months{{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}}; - -template -class b_formatter : public flag_formatter -{ -public: - explicit b_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - string_view_t field_value{months[static_cast(tm_time.tm_mon)]}; - ScopedPadder p(field_value.size(), padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Full month name -static const std::array full_months{ - {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}}; - -template -class B_formatter : public flag_formatter -{ -public: - explicit B_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - string_view_t field_value{full_months[static_cast(tm_time.tm_mon)]}; - ScopedPadder p(field_value.size(), padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Date and time representation (Thu Aug 23 15:35:46 2014) -template -class c_formatter final : public flag_formatter -{ -public: - explicit c_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 24; - ScopedPadder p(field_size, padinfo_, dest); - - fmt_helper::append_string_view(days[static_cast(tm_time.tm_wday)], dest); - dest.push_back(' '); - fmt_helper::append_string_view(months[static_cast(tm_time.tm_mon)], dest); - dest.push_back(' '); - fmt_helper::append_int(tm_time.tm_mday, dest); - dest.push_back(' '); - // time - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - dest.push_back(' '); - fmt_helper::append_int(tm_time.tm_year + 1900, dest); - } -}; - -// year - 2 digit -template -class C_formatter final : public flag_formatter -{ -public: - explicit C_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_year % 100, dest); - } -}; - -// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 -template -class D_formatter final : public flag_formatter -{ -public: - explicit D_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 10; - ScopedPadder p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_mon + 1, dest); - dest.push_back('/'); - fmt_helper::pad2(tm_time.tm_mday, dest); - dest.push_back('/'); - fmt_helper::pad2(tm_time.tm_year % 100, dest); - } -}; - -// year - 4 digit -template -class Y_formatter final : public flag_formatter -{ -public: - explicit Y_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 4; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::append_int(tm_time.tm_year + 1900, dest); - } -}; - -// month 1-12 -template -class m_formatter final : public flag_formatter -{ -public: - explicit m_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_mon + 1, dest); - } -}; - -// day of month 1-31 -template -class d_formatter final : public flag_formatter -{ -public: - explicit d_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_mday, dest); - } -}; - -// hours in 24 format 0-23 -template -class H_formatter final : public flag_formatter -{ -public: - explicit H_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_hour, dest); - } -}; - -// hours in 12 format 1-12 -template -class I_formatter final : public flag_formatter -{ -public: - explicit I_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(to12h(tm_time), dest); - } -}; - -// minutes 0-59 -template -class M_formatter final : public flag_formatter -{ -public: - explicit M_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_min, dest); - } -}; - -// seconds 0-59 -template -class S_formatter final : public flag_formatter -{ -public: - explicit S_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_sec, dest); - } -}; - -// milliseconds -template -class e_formatter final : public flag_formatter -{ -public: - explicit e_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - auto millis = fmt_helper::time_fraction(msg.time); - const size_t field_size = 3; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad3(static_cast(millis.count()), dest); - } -}; - -// microseconds -template -class f_formatter final : public flag_formatter -{ -public: - explicit f_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - auto micros = fmt_helper::time_fraction(msg.time); - - const size_t field_size = 6; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad6(static_cast(micros.count()), dest); - } -}; - -// nanoseconds -template -class F_formatter final : public flag_formatter -{ -public: - explicit F_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - auto ns = fmt_helper::time_fraction(msg.time); - const size_t field_size = 9; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::pad9(static_cast(ns.count()), dest); - } -}; - -// seconds since epoch -template -class E_formatter final : public flag_formatter -{ -public: - explicit E_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - const size_t field_size = 10; - ScopedPadder p(field_size, padinfo_, dest); - auto duration = msg.time.time_since_epoch(); - auto seconds = std::chrono::duration_cast(duration).count(); - fmt_helper::append_int(seconds, dest); - } -}; - -// AM/PM -template -class p_formatter final : public flag_formatter -{ -public: - explicit p_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 2; - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::append_string_view(ampm(tm_time), dest); - } -}; - -// 12 hour clock 02:55:02 pm -template -class r_formatter final : public flag_formatter -{ -public: - explicit r_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 11; - ScopedPadder p(field_size, padinfo_, dest); - - fmt_helper::pad2(to12h(tm_time), dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - dest.push_back(' '); - fmt_helper::append_string_view(ampm(tm_time), dest); - } -}; - -// 24-hour HH:MM time, equivalent to %H:%M -template -class R_formatter final : public flag_formatter -{ -public: - explicit R_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 5; - ScopedPadder p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - } -}; - -// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S -template -class T_formatter final : public flag_formatter -{ -public: - explicit T_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 8; - ScopedPadder p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - } -}; - -// ISO 8601 offset from UTC in timezone (+-HH:MM) -template -class z_formatter final : public flag_formatter -{ -public: - explicit z_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - z_formatter() = default; - z_formatter(const z_formatter &) = delete; - z_formatter &operator=(const z_formatter &) = delete; - - void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override - { - const size_t field_size = 6; - ScopedPadder p(field_size, padinfo_, dest); - - auto total_minutes = get_cached_offset(msg, tm_time); - bool is_negative = total_minutes < 0; - if (is_negative) - { - total_minutes = -total_minutes; - dest.push_back('-'); - } - else - { - dest.push_back('+'); - } - - fmt_helper::pad2(total_minutes / 60, dest); // hours - dest.push_back(':'); - fmt_helper::pad2(total_minutes % 60, dest); // minutes - } - -private: - log_clock::time_point last_update_{std::chrono::seconds(0)}; - int offset_minutes_{0}; - - int get_cached_offset(const log_msg &msg, const std::tm &tm_time) - { - // refresh every 10 seconds - if (msg.time - last_update_ >= std::chrono::seconds(10)) - { - offset_minutes_ = os::utc_minutes_offset(tm_time); - last_update_ = msg.time; - } - return offset_minutes_; - } -}; - -// Thread id -template -class t_formatter final : public flag_formatter -{ -public: - explicit t_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - const auto field_size = fmt_helper::count_digits(msg.thread_id); - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::append_int(msg.thread_id, dest); - } -}; - -// Current pid -template -class pid_formatter final : public flag_formatter -{ -public: - explicit pid_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override - { - const auto pid = static_cast(details::os::pid()); - auto field_size = fmt_helper::count_digits(pid); - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::append_int(pid, dest); - } -}; - -template -class v_formatter final : public flag_formatter -{ -public: - explicit v_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - ScopedPadder p(msg.payload.size(), padinfo_, dest); - fmt_helper::append_string_view(msg.payload, dest); - } -}; - -class ch_formatter final : public flag_formatter -{ -public: - explicit ch_formatter(char ch) - : ch_(ch) - {} - - void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override - { - dest.push_back(ch_); - } - -private: - char ch_; -}; - -// aggregate user chars to display as is -class aggregate_formatter final : public flag_formatter -{ -public: - aggregate_formatter() = default; - - void add_ch(char ch) - { - str_ += ch; - } - void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override - { - fmt_helper::append_string_view(str_, dest); - } - -private: - std::string str_; -}; - -// mark the color range. expect it to be in the form of "%^colored text%$" -class color_start_formatter final : public flag_formatter -{ -public: - explicit color_start_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - msg.color_range_start = dest.size(); - } -}; - -class color_stop_formatter final : public flag_formatter -{ -public: - explicit color_stop_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - msg.color_range_end = dest.size(); - } -}; - -// print source location -template -class source_location_formatter final : public flag_formatter -{ -public: - explicit source_location_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - if (msg.source.empty()) - { - return; - } - - size_t text_size = - padinfo_.enabled() ? std::char_traits::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1 : 0; - - ScopedPadder p(text_size, padinfo_, dest); - fmt_helper::append_string_view(msg.source.filename, dest); - dest.push_back(':'); - fmt_helper::append_int(msg.source.line, dest); - } -}; - -// print source filename -template -class source_filename_formatter final : public flag_formatter -{ -public: - explicit source_filename_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - if (msg.source.empty()) - { - return; - } - size_t text_size = padinfo_.enabled() ? std::char_traits::length(msg.source.filename) : 0; - ScopedPadder p(text_size, padinfo_, dest); - fmt_helper::append_string_view(msg.source.filename, dest); - } -}; - -template -class short_filename_formatter final : public flag_formatter -{ -public: - explicit short_filename_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - static const char *basename(const char *filename) - { - const char *rv = std::strrchr(filename, os::folder_sep); - return rv != nullptr ? rv + 1 : filename; - } - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - if (msg.source.empty()) - { - return; - } - auto filename = basename(msg.source.filename); - size_t text_size = padinfo_.enabled() ? std::char_traits::length(filename) : 0; - ScopedPadder p(text_size, padinfo_, dest); - fmt_helper::append_string_view(filename, dest); - } -}; - -template -class source_linenum_formatter final : public flag_formatter -{ -public: - explicit source_linenum_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - if (msg.source.empty()) - { - return; - } - - auto field_size = fmt_helper::count_digits(msg.source.line); - ScopedPadder p(field_size, padinfo_, dest); - fmt_helper::append_int(msg.source.line, dest); - } -}; - -// print source funcname -template -class source_funcname_formatter final : public flag_formatter -{ -public: - explicit source_funcname_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - if (msg.source.empty()) - { - return; - } - size_t text_size = padinfo_.enabled() ? std::char_traits::length(msg.source.funcname) : 0; - ScopedPadder p(text_size, padinfo_, dest); - fmt_helper::append_string_view(msg.source.funcname, dest); - } -}; - -// print elapsed time since last message -template - -class elapsed_formatter final : public flag_formatter -{ -public: - using DurationUnits = Units; - - explicit elapsed_formatter(padding_info padinfo) - : flag_formatter(padinfo) - , last_message_time_(log_clock::now()) - {} - - void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override - { - auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero()); - auto delta_units = std::chrono::duration_cast(delta); - last_message_time_ = msg.time; - auto delta_count = static_cast(delta_units.count()); - auto n_digits = static_cast(fmt_helper::count_digits(delta_count)); - ScopedPadder p(n_digits, padinfo_, dest); - fmt_helper::append_int(delta_count, dest); - } - -private: - log_clock::time_point last_message_time_; -}; - -// Full info formatter -// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter final : public flag_formatter -{ -public: - explicit full_formatter(padding_info padinfo) - : flag_formatter(padinfo) - {} - - void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override - { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::seconds; - - // cache the date/time part for the next second. - auto duration = msg.time.time_since_epoch(); - auto secs = duration_cast(duration); - - if (cache_timestamp_ != secs || cached_datetime_.size() == 0) - { - cached_datetime_.clear(); - cached_datetime_.push_back('['); - fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); - cached_datetime_.push_back('-'); - - fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); - cached_datetime_.push_back('-'); - - fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); - cached_datetime_.push_back(' '); - - fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); - cached_datetime_.push_back(':'); - - fmt_helper::pad2(tm_time.tm_min, cached_datetime_); - cached_datetime_.push_back(':'); - - fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); - cached_datetime_.push_back('.'); - - cache_timestamp_ = secs; - } - dest.append(cached_datetime_.begin(), cached_datetime_.end()); - - auto millis = fmt_helper::time_fraction(msg.time); - fmt_helper::pad3(static_cast(millis.count()), dest); - dest.push_back(']'); - dest.push_back(' '); - -#ifndef SPDLOG_NO_NAME - if (msg.logger_name.size() > 0) - { - dest.push_back('['); - // fmt_helper::append_str(*msg.logger_name, dest); - fmt_helper::append_string_view(msg.logger_name, dest); - dest.push_back(']'); - dest.push_back(' '); - } -#endif - dest.push_back('['); - // wrap the level name with color - msg.color_range_start = dest.size(); - // fmt_helper::append_string_view(level::to_c_str(msg.level), dest); - fmt_helper::append_string_view(level::to_string_view(msg.level), dest); - msg.color_range_end = dest.size(); - dest.push_back(']'); - dest.push_back(' '); - - // add source location if present - if (!msg.source.empty()) - { - dest.push_back('['); - const char *filename = details::short_filename_formatter::basename(msg.source.filename); - fmt_helper::append_string_view(filename, dest); - dest.push_back(':'); - fmt_helper::append_int(msg.source.line, dest); - dest.push_back(']'); - dest.push_back(' '); - } - // fmt_helper::append_string_view(msg.msg(), dest); - fmt_helper::append_string_view(msg.payload, dest); - } - -private: - std::chrono::seconds cache_timestamp_{0}; - memory_buf_t cached_datetime_; -}; - -} // namespace details - -SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern, pattern_time_type time_type, std::string eol) - : pattern_(std::move(pattern)) - , eol_(std::move(eol)) - , pattern_time_type_(time_type) - , last_log_secs_(0) -{ - std::memset(&cached_tm_, 0, sizeof(cached_tm_)); - compile_pattern_(pattern_); -} - -// use by default full formatter for if pattern is not given -SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol) - : pattern_("%+") - , eol_(std::move(eol)) - , pattern_time_type_(time_type) - , last_log_secs_(0) -{ - std::memset(&cached_tm_, 0, sizeof(cached_tm_)); - formatters_.push_back(details::make_unique(details::padding_info{})); -} - -SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const -{ - return details::make_unique(pattern_, pattern_time_type_, eol_); -} - -SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) -{ - auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); - if (secs != last_log_secs_) - { - cached_tm_ = get_time_(msg); - last_log_secs_ = secs; - } - - for (auto &f : formatters_) - { - f->format(msg, cached_tm_, dest); - } - // write eol - details::fmt_helper::append_string_view(eol_, dest); -} - -SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) -{ - if (pattern_time_type_ == pattern_time_type::local) - { - return details::os::localtime(log_clock::to_time_t(msg.time)); - } - return details::os::gmtime(log_clock::to_time_t(msg.time)); -} - -template -SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding) -{ - switch (flag) - { - - case ('+'): // default formatter - formatters_.push_back(details::make_unique(padding)); - break; - - case 'n': // logger name - formatters_.push_back(details::make_unique>(padding)); - break; - - case 'l': // level - formatters_.push_back(details::make_unique>(padding)); - break; - - case 'L': // short level - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('t'): // thread id - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('v'): // the message text - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('a'): // weekday - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('A'): // short weekday - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('b'): - case ('h'): // month - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('B'): // short month - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('c'): // datetime - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('C'): // year 2 digits - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('Y'): // year 4 digits - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('D'): - case ('x'): // datetime MM/DD/YY - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('m'): // month 1-12 - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('d'): // day of month 1-31 - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('H'): // hours 24 - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('I'): // hours 12 - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('M'): // minutes - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('S'): // seconds - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('e'): // milliseconds - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('f'): // microseconds - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('F'): // nanoseconds - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('E'): // seconds since epoch - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('p'): // am/pm - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('r'): // 12 hour clock 02:55:02 pm - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('R'): // 24-hour HH:MM time - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('T'): - case ('X'): // ISO 8601 time format (HH:MM:SS) - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('z'): // timezone - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('P'): // pid - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('^'): // color range start - formatters_.push_back(details::make_unique(padding)); - break; - - case ('$'): // color range end - formatters_.push_back(details::make_unique(padding)); - break; - - case ('@'): // source location (filename:filenumber) - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('s'): // short source filename - without directory name - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('g'): // full source filename - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('#'): // source line number - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('!'): // source funcname - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('%'): // % char - formatters_.push_back(details::make_unique('%')); - break; - - case ('u'): // elapsed time since last log message in nanos - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('i'): // elapsed time since last log message in micros - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('o'): // elapsed time since last log message in millis - formatters_.push_back(details::make_unique>(padding)); - break; - - case ('O'): // elapsed time since last log message in seconds - formatters_.push_back(details::make_unique>(padding)); - break; - - default: // Unknown flag appears as is - auto unknown_flag = details::make_unique(); - unknown_flag->add_ch('%'); - unknown_flag->add_ch(flag); - formatters_.push_back((std::move(unknown_flag))); - break; - } -} - -// Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X) -// Advance the given it pass the end of the padding spec found (if any) -// Return padding. -SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end) -{ - using details::padding_info; - using details::scoped_padder; - const size_t max_width = 64; - if (it == end) - { - return padding_info{}; - } - - padding_info::pad_side side; - switch (*it) - { - case '-': - side = padding_info::right; - ++it; - break; - case '=': - side = padding_info::center; - ++it; - break; - default: - side = details::padding_info::left; - break; - } - - if (it == end || !std::isdigit(static_cast(*it))) - { - return padding_info{}; // no padding if no digit found here - } - - auto width = static_cast(*it) - '0'; - for (++it; it != end && std::isdigit(static_cast(*it)); ++it) - { - auto digit = static_cast(*it) - '0'; - width = width * 10 + digit; - } - - // search for the optional truncate marker '!' - bool truncate; - if (it != end && *it == '!') - { - truncate = true; - ++it; - } - else - { - truncate = false; - } - - return details::padding_info{std::min(width, max_width), side, truncate}; -} - -SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern) -{ - auto end = pattern.end(); - std::unique_ptr user_chars; - formatters_.clear(); - for (auto it = pattern.begin(); it != end; ++it) - { - if (*it == '%') - { - if (user_chars) // append user chars found so far - { - formatters_.push_back(std::move(user_chars)); - } - - auto padding = handle_padspec_(++it, end); - - if (it != end) - { - if (padding.enabled()) - { - handle_flag_(*it, padding); - } - else - { - handle_flag_(*it, padding); - } - } - else - { - break; - } - } - else // chars not following the % sign should be displayed as is - { - if (!user_chars) - { - user_chars = details::make_unique(); - } - user_chars->add_ch(*it); - } - } - if (user_chars) // append raw chars found so far - { - formatters_.push_back(std::move(user_chars)); - } -} -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/pattern_formatter.h b/thirdparty/spdlog/include/spdlog/details/pattern_formatter.h deleted file mode 100644 index fa134ad..0000000 --- a/thirdparty/spdlog/include/spdlog/details/pattern_formatter.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace spdlog { -namespace details { - -// padding information. -struct padding_info -{ - enum pad_side - { - left, - right, - center - }; - - padding_info() = default; - padding_info(size_t width, padding_info::pad_side side, bool truncate) - : width_(width) - , side_(side) - , truncate_(truncate) - , enabled_(true) - {} - - bool enabled() const - { - return enabled_; - } - const size_t width_ = 0; - const pad_side side_ = left; - bool truncate_ = false; - bool enabled_ = false; -}; - -class flag_formatter -{ -public: - explicit flag_formatter(padding_info padinfo) - : padinfo_(padinfo) - {} - flag_formatter() = default; - virtual ~flag_formatter() = default; - virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0; - -protected: - padding_info padinfo_; -}; - -} // namespace details - -class pattern_formatter final : public formatter -{ -public: - explicit pattern_formatter( - std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); - - // use default pattern is not given - explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); - - pattern_formatter(const pattern_formatter &other) = delete; - pattern_formatter &operator=(const pattern_formatter &other) = delete; - - std::unique_ptr clone() const override; - void format(const details::log_msg &msg, memory_buf_t &dest) override; - -private: - std::string pattern_; - std::string eol_; - pattern_time_type pattern_time_type_; - std::tm cached_tm_; - std::chrono::seconds last_log_secs_; - std::vector> formatters_; - - std::tm get_time_(const details::log_msg &msg); - template - void handle_flag_(char flag, details::padding_info padding); - - // Extract given pad spec (e.g. %8X) - // Advance the given it pass the end of the padding spec found (if any) - // Return padding. - details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end); - - void compile_pattern_(const std::string &pattern); -}; -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "pattern_formatter-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/periodic_worker-inl.h b/thirdparty/spdlog/include/spdlog/details/periodic_worker-inl.h deleted file mode 100644 index 1d79499..0000000 --- a/thirdparty/spdlog/include/spdlog/details/periodic_worker-inl.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -namespace spdlog { -namespace details { - -SPDLOG_INLINE periodic_worker::periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) -{ - active_ = (interval > std::chrono::seconds::zero()); - if (!active_) - { - return; - } - - worker_thread_ = std::thread([this, callback_fun, interval]() { - for (;;) - { - std::unique_lock lock(this->mutex_); - if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) - { - return; // active_ == false, so exit this thread - } - callback_fun(); - } - }); -} - -// stop the worker thread and join it -SPDLOG_INLINE periodic_worker::~periodic_worker() -{ - if (worker_thread_.joinable()) - { - { - std::lock_guard lock(mutex_); - active_ = false; - } - cv_.notify_one(); - worker_thread_.join(); - } -} - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/periodic_worker.h b/thirdparty/spdlog/include/spdlog/details/periodic_worker.h deleted file mode 100644 index d3b5c63..0000000 --- a/thirdparty/spdlog/include/spdlog/details/periodic_worker.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// periodic worker thread - periodically executes the given callback function. -// -// RAII over the owned thread: -// creates the thread on construction. -// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first). - -#include -#include -#include -#include -#include -namespace spdlog { -namespace details { - -class periodic_worker -{ -public: - periodic_worker(const std::function &callback_fun, std::chrono::seconds interval); - periodic_worker(const periodic_worker &) = delete; - periodic_worker &operator=(const periodic_worker &) = delete; - // stop the worker thread and join it - ~periodic_worker(); - -private: - bool active_; - std::thread worker_thread_; - std::mutex mutex_; - std::condition_variable cv_; -}; -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "periodic_worker-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/registry-inl.h b/thirdparty/spdlog/include/spdlog/details/registry-inl.h deleted file mode 100644 index 562aa06..0000000 --- a/thirdparty/spdlog/include/spdlog/details/registry-inl.h +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include -#include -#include -#include - -#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER -// support for the default stdout color logger -#ifdef _WIN32 -#include -#else -#include -#endif -#endif // SPDLOG_DISABLE_DEFAULT_LOGGER - -#include -#include -#include -#include -#include - -namespace spdlog { -namespace details { - -SPDLOG_INLINE registry::registry() - : formatter_(new pattern_formatter()) -{ - -#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER - // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows). -#ifdef _WIN32 - auto color_sink = std::make_shared(); -#else - auto color_sink = std::make_shared(); -#endif - - const char *default_logger_name = ""; - default_logger_ = std::make_shared(default_logger_name, std::move(color_sink)); - loggers_[default_logger_name] = default_logger_; - -#endif // SPDLOG_DISABLE_DEFAULT_LOGGER -} -SPDLOG_INLINE void registry::register_logger(std::shared_ptr new_logger) -{ - std::lock_guard lock(logger_map_mutex_); - register_logger_(std::move(new_logger)); -} - -SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr new_logger) -{ - std::lock_guard lock(logger_map_mutex_); - new_logger->set_formatter(formatter_->clone()); - - if (err_handler_) - { - new_logger->set_error_handler(err_handler_); - } - - new_logger->set_level(level_); - new_logger->flush_on(flush_level_); - - if (backtrace_n_messages_ > 0) - { - new_logger->enable_backtrace(backtrace_n_messages_); - } - - if (automatic_registration_) - { - register_logger_(std::move(new_logger)); - } -} - -SPDLOG_INLINE std::shared_ptr registry::get(const std::string &logger_name) -{ - std::lock_guard lock(logger_map_mutex_); - auto found = loggers_.find(logger_name); - return found == loggers_.end() ? nullptr : found->second; -} - -SPDLOG_INLINE std::shared_ptr registry::default_logger() -{ - std::lock_guard lock(logger_map_mutex_); - return default_logger_; -} - -// Return raw ptr to the default logger. -// To be used directly by the spdlog default api (e.g. spdlog::info) -// This make the default API faster, but cannot be used concurrently with set_default_logger(). -// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. -SPDLOG_INLINE logger *registry::get_default_raw() -{ - return default_logger_.get(); -} - -// set default logger. -// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. -SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr new_default_logger) -{ - std::lock_guard lock(logger_map_mutex_); - // remove previous default logger from the map - if (default_logger_ != nullptr) - { - loggers_.erase(default_logger_->name()); - } - if (new_default_logger != nullptr) - { - loggers_[new_default_logger->name()] = new_default_logger; - } - default_logger_ = std::move(new_default_logger); -} - -SPDLOG_INLINE void registry::set_tp(std::shared_ptr tp) -{ - std::lock_guard lock(tp_mutex_); - tp_ = std::move(tp); -} - -SPDLOG_INLINE std::shared_ptr registry::get_tp() -{ - std::lock_guard lock(tp_mutex_); - return tp_; -} - -// Set global formatter. Each sink in each logger will get a clone of this object -SPDLOG_INLINE void registry::set_formatter(std::unique_ptr formatter) -{ - std::lock_guard lock(logger_map_mutex_); - formatter_ = std::move(formatter); - for (auto &l : loggers_) - { - l.second->set_formatter(formatter_->clone()); - } -} - -SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) -{ - std::lock_guard lock(logger_map_mutex_); - backtrace_n_messages_ = n_messages; - - for (auto &l : loggers_) - { - l.second->enable_backtrace(n_messages); - } -} - -SPDLOG_INLINE void registry::disable_backtrace() -{ - std::lock_guard lock(logger_map_mutex_); - backtrace_n_messages_ = 0; - for (auto &l : loggers_) - { - l.second->disable_backtrace(); - } -} - -SPDLOG_INLINE void registry::set_level(level::level_enum log_level) -{ - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->set_level(log_level); - } - level_ = log_level; -} - -SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) -{ - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->flush_on(log_level); - } - flush_level_ = log_level; -} - -SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval) -{ - std::lock_guard lock(flusher_mutex_); - std::function clbk = std::bind(®istry::flush_all, this); - periodic_flusher_ = details::make_unique(clbk, interval); -} - -SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg)) -{ - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->set_error_handler(handler); - } - err_handler_ = handler; -} - -SPDLOG_INLINE void registry::apply_all(const std::function)> &fun) -{ - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - fun(l.second); - } -} - -SPDLOG_INLINE void registry::flush_all() -{ - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->flush(); - } -} - -SPDLOG_INLINE void registry::drop(const std::string &logger_name) -{ - std::lock_guard lock(logger_map_mutex_); - loggers_.erase(logger_name); - if (default_logger_ && default_logger_->name() == logger_name) - { - default_logger_.reset(); - } -} - -SPDLOG_INLINE void registry::drop_all() -{ - std::lock_guard lock(logger_map_mutex_); - loggers_.clear(); - default_logger_.reset(); -} - -// clean all resources and threads started by the registry -SPDLOG_INLINE void registry::shutdown() -{ - { - std::lock_guard lock(flusher_mutex_); - periodic_flusher_.reset(); - } - - drop_all(); - - { - std::lock_guard lock(tp_mutex_); - tp_.reset(); - } -} - -SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex() -{ - return tp_mutex_; -} - -SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration) -{ - std::lock_guard lock(logger_map_mutex_); - automatic_registration_ = automatic_registration; -} - -SPDLOG_INLINE registry ®istry::instance() -{ - static registry s_instance; - return s_instance; -} - -SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) -{ - if (loggers_.find(logger_name) != loggers_.end()) - { - SPDLOG_THROW(spdlog_ex("logger with name '" + logger_name + "' already exists")); - } -} - -SPDLOG_INLINE void registry::register_logger_(std::shared_ptr new_logger) -{ - auto logger_name = new_logger->name(); - throw_if_exists_(logger_name); - loggers_[logger_name] = std::move(new_logger); -} -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/registry.h b/thirdparty/spdlog/include/spdlog/details/registry.h deleted file mode 100644 index 6ac571d..0000000 --- a/thirdparty/spdlog/include/spdlog/details/registry.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// Loggers registry of unique name->logger pointer -// An attempt to create a logger with an already existing name will result with spdlog_ex exception. -// If user requests a non existing logger, nullptr will be returned -// This class is thread safe - -#include - -#include -#include -#include -#include -#include -#include - -namespace spdlog { -class logger; - -namespace details { -class thread_pool; -class periodic_worker; - -class registry -{ -public: - registry(const registry &) = delete; - registry &operator=(const registry &) = delete; - - void register_logger(std::shared_ptr new_logger); - void initialize_logger(std::shared_ptr new_logger); - std::shared_ptr get(const std::string &logger_name); - std::shared_ptr default_logger(); - - // Return raw ptr to the default logger. - // To be used directly by the spdlog default api (e.g. spdlog::info) - // This make the default API faster, but cannot be used concurrently with set_default_logger(). - // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. - logger *get_default_raw(); - - // set default logger. - // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. - void set_default_logger(std::shared_ptr new_default_logger); - - void set_tp(std::shared_ptr tp); - - std::shared_ptr get_tp(); - - // Set global formatter. Each sink in each logger will get a clone of this object - void set_formatter(std::unique_ptr formatter); - - void enable_backtrace(size_t n_messages); - - void disable_backtrace(); - - void set_level(level::level_enum log_level); - - void flush_on(level::level_enum log_level); - - void flush_every(std::chrono::seconds interval); - - void set_error_handler(void (*handler)(const std::string &msg)); - - void apply_all(const std::function)> &fun); - - void flush_all(); - - void drop(const std::string &logger_name); - - void drop_all(); - - // clean all resources and threads started by the registry - void shutdown(); - - std::recursive_mutex &tp_mutex(); - - void set_automatic_registration(bool automatic_registration); - - static registry &instance(); - -private: - registry(); - ~registry() = default; - - void throw_if_exists_(const std::string &logger_name); - void register_logger_(std::shared_ptr new_logger); - std::mutex logger_map_mutex_, flusher_mutex_; - std::recursive_mutex tp_mutex_; - std::unordered_map> loggers_; - std::unique_ptr formatter_; - level::level_enum level_ = level::info; - level::level_enum flush_level_ = level::off; - void (*err_handler_)(const std::string &msg); - std::shared_ptr tp_; - std::unique_ptr periodic_flusher_; - std::shared_ptr default_logger_; - bool automatic_registration_ = true; - size_t backtrace_n_messages_ = 0; -}; - -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "registry-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/details/synchronous_factory.h b/thirdparty/spdlog/include/spdlog/details/synchronous_factory.h deleted file mode 100644 index 68f5c21..0000000 --- a/thirdparty/spdlog/include/spdlog/details/synchronous_factory.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include "registry.h" - -namespace spdlog { - -// Default logger factory- creates synchronous loggers -class logger; - -struct synchronous_factory -{ - template - static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) - { - auto sink = std::make_shared(std::forward(args)...); - auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); - details::registry::instance().initialize_logger(new_logger); - return new_logger; - } -}; -} // namespace spdlog \ No newline at end of file diff --git a/thirdparty/spdlog/include/spdlog/details/thread_pool-inl.h b/thirdparty/spdlog/include/spdlog/details/thread_pool-inl.h deleted file mode 100644 index 43220f4..0000000 --- a/thirdparty/spdlog/include/spdlog/details/thread_pool-inl.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#ifndef SPDLOG_HEADER_ONLY -#include -#endif - -#include -#include - -namespace spdlog { -namespace details { - -SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start) - : q_(q_max_items) -{ - if (threads_n == 0 || threads_n > 1000) - { - SPDLOG_THROW(spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " - "range is 1-1000)")); - } - for (size_t i = 0; i < threads_n; i++) - { - threads_.emplace_back([this, on_thread_start] { - on_thread_start(); - this->thread_pool::worker_loop_(); - }); - } -} - -SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) - : thread_pool(q_max_items, threads_n, [] {}) -{} - -// message all threads to terminate gracefully join them -SPDLOG_INLINE thread_pool::~thread_pool() -{ - SPDLOG_TRY - { - for (size_t i = 0; i < threads_.size(); i++) - { - post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); - } - - for (auto &t : threads_) - { - t.join(); - } - } - SPDLOG_CATCH_ALL() {} -} - -void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy) -{ - async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg); - post_async_msg_(std::move(async_m), overflow_policy); -} - -void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) -{ - post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); -} - -size_t SPDLOG_INLINE thread_pool::overrun_counter() -{ - return q_.overrun_counter(); -} - -void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) -{ - if (overflow_policy == async_overflow_policy::block) - { - q_.enqueue(std::move(new_msg)); - } - else - { - q_.enqueue_nowait(std::move(new_msg)); - } -} - -void SPDLOG_INLINE thread_pool::worker_loop_() -{ - while (process_next_msg_()) {} -} - -// process next message in the queue -// return true if this thread should still be active (while no terminate msg -// was received) -bool SPDLOG_INLINE thread_pool::process_next_msg_() -{ - async_msg incoming_async_msg; - bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); - if (!dequeued) - { - return true; - } - - switch (incoming_async_msg.msg_type) - { - case async_msg_type::log: { - incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg); - return true; - } - case async_msg_type::flush: { - incoming_async_msg.worker_ptr->backend_flush_(); - return true; - } - - case async_msg_type::terminate: { - return false; - } - - default: { - assert(false && "Unexpected async_msg_type"); - } - } - - return true; -} - -} // namespace details -} // namespace spdlog diff --git a/thirdparty/spdlog/include/spdlog/details/thread_pool.h b/thirdparty/spdlog/include/spdlog/details/thread_pool.h deleted file mode 100644 index 1207804..0000000 --- a/thirdparty/spdlog/include/spdlog/details/thread_pool.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace spdlog { -class async_logger; - -namespace details { - -using async_logger_ptr = std::shared_ptr; - -enum class async_msg_type -{ - log, - flush, - terminate -}; - -#include -// Async msg to move to/from the queue -// Movable only. should never be copied -struct async_msg : log_msg_buffer -{ - async_msg_type msg_type{async_msg_type::log}; - async_logger_ptr worker_ptr; - - async_msg() = default; - ~async_msg() = default; - - // should only be moved in or out of the queue.. - async_msg(const async_msg &) = delete; - -// support for vs2013 move -#if defined(_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&other) - : log_msg_buffer(std::move(other)) - , msg_type(other.msg_type) - , worker_ptr(std::move(other.worker_ptr)) - {} - - async_msg &operator=(async_msg &&other) - { - *static_cast(this) = std::move(other); - msg_type = other.msg_type; - worker_ptr = std::move(other.worker_ptr); - return *this; - } -#else // (_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&) = default; - async_msg &operator=(async_msg &&) = default; -#endif - - // construct from log_msg with given type - async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m) - : log_msg_buffer{m} - , msg_type{the_type} - , worker_ptr{std::move(worker)} - {} - - async_msg(async_logger_ptr &&worker, async_msg_type the_type) - : log_msg_buffer{} - , msg_type{the_type} - , worker_ptr{std::move(worker)} - {} - - explicit async_msg(async_msg_type the_type) - : async_msg{nullptr, the_type} - {} -}; - -class thread_pool -{ -public: - using item_type = async_msg; - using q_type = details::mpmc_blocking_queue; - - thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); - thread_pool(size_t q_max_items, size_t threads_n); - - // message all threads to terminate gracefully join them - ~thread_pool(); - - thread_pool(const thread_pool &) = delete; - thread_pool &operator=(thread_pool &&) = delete; - - void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy); - void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); - size_t overrun_counter(); - -private: - q_type q_; - - std::vector threads_; - - void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy); - void worker_loop_(); - - // process next message in the queue - // return true if this thread should still be active (while no terminate msg - // was received) - bool process_next_msg_(); -}; - -} // namespace details -} // namespace spdlog - -#ifdef SPDLOG_HEADER_ONLY -#include "thread_pool-inl.h" -#endif diff --git a/thirdparty/spdlog/include/spdlog/fmt/bin_to_hex.h b/thirdparty/spdlog/include/spdlog/fmt/bin_to_hex.h deleted file mode 100644 index de12606..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bin_to_hex.h +++ /dev/null @@ -1,175 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// -// Support for logging binary data as hex -// format flags: -// {:X} - print in uppercase. -// {:s} - don't separate each byte with space. -// {:p} - don't print the position on each line start. -// {:n} - don't split the output to lines. - -// -// Examples: -// -// std::vector v(200, 0x0b); -// logger->info("Some buffer {}", spdlog::to_hex(v)); -// char buf[128]; -// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf))); - -namespace spdlog { -namespace details { - -template -class bytes_range -{ -public: - bytes_range(It range_begin, It range_end) - : begin_(range_begin) - , end_(range_end) - {} - - It begin() const - { - return begin_; - } - It end() const - { - return end_; - } - -private: - It begin_, end_; -}; -} // namespace details - -// create a bytes_range that wraps the given container -template -inline details::bytes_range to_hex(const Container &container) -{ - static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); - using Iter = typename Container::const_iterator; - return details::bytes_range(std::begin(container), std::end(container)); -} - -// create bytes_range from ranges -template -inline details::bytes_range to_hex(const It range_begin, const It range_end) -{ - return details::bytes_range(range_begin, range_end); -} - -} // namespace spdlog - -namespace fmt { - -template -struct formatter> -{ - const std::size_t line_size = 100; - const char delimiter = ' '; - - bool put_newlines = true; - bool put_delimiters = true; - bool use_uppercase = false; - bool put_positions = true; // position on start of each line - - // parse the format string flags - template - auto parse(ParseContext &ctx) -> decltype(ctx.begin()) - { - auto it = ctx.begin(); - while (*it && *it != '}') - { - switch (*it) - { - case 'X': - use_uppercase = true; - break; - case 's': - put_delimiters = false; - break; - case 'p': - put_positions = false; - break; - case 'n': - put_newlines = false; - break; - } - - ++it; - } - return it; - } - - // format the given bytes range as hex - template - auto format(const spdlog::details::bytes_range &the_range, FormatContext &ctx) -> decltype(ctx.out()) - { - SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF"; - SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef"; - const char *hex_chars = use_uppercase ? hex_upper : hex_lower; - - std::size_t pos = 0; - std::size_t column = line_size; -#if FMT_VERSION < 60000 - auto inserter = ctx.begin(); -#else - auto inserter = ctx.out(); -#endif - - for (auto &item : the_range) - { - auto ch = static_cast(item); - pos++; - - if (put_newlines && column >= line_size) - { - column = put_newline(inserter, pos); - - // put first byte without delimiter in front of it - *inserter++ = hex_chars[(ch >> 4) & 0x0f]; - *inserter++ = hex_chars[ch & 0x0f]; - column += 2; - continue; - } - - if (put_delimiters) - { - *inserter++ = delimiter; - ++column; - } - - *inserter++ = hex_chars[(ch >> 4) & 0x0f]; - *inserter++ = hex_chars[ch & 0x0f]; - column += 2; - } - return inserter; - } - - // put newline(and position header) - // return the next column - template - std::size_t put_newline(It inserter, std::size_t pos) - { -#ifdef _WIN32 - *inserter++ = '\r'; -#endif - *inserter++ = '\n'; - - if (put_positions) - { - fmt::format_to(inserter, "{:<04X}: ", pos - 1); - return 7; - } - else - { - return 1; - } - } -}; -} // namespace fmt diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/LICENSE.rst b/thirdparty/spdlog/include/spdlog/fmt/bundled/LICENSE.rst deleted file mode 100644 index f0ec3db..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/LICENSE.rst +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 - present, Victor Zverovich - -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. - ---- Optional exception to the license --- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into a machine-executable object form of such -source code, you may redistribute such embedded portions in such object form -without including the above copyright and permission notices. diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/chrono.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/chrono.h deleted file mode 100644 index ca4ed30..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/chrono.h +++ /dev/null @@ -1,1106 +0,0 @@ -// Formatting library for C++ - chrono support -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_CHRONO_H_ -#define FMT_CHRONO_H_ - -#include "format.h" -#include "locale.h" - -#include -#include -#include -#include - -FMT_BEGIN_NAMESPACE - -// Enable safe chrono durations, unless explicitly disabled. -#ifndef FMT_SAFE_DURATION_CAST -# define FMT_SAFE_DURATION_CAST 1 -#endif -#if FMT_SAFE_DURATION_CAST - -// For conversion between std::chrono::durations without undefined -// behaviour or erroneous results. -// This is a stripped down version of duration_cast, for inclusion in fmt. -// See https://github.com/pauldreik/safe_duration_cast -// -// Copyright Paul Dreik 2019 -namespace safe_duration_cast { - -template ::value && - std::numeric_limits::is_signed == - std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; - static_assert(F::is_integer, "From must be integral"); - static_assert(T::is_integer, "To must be integral"); - - // A and B are both signed, or both unsigned. - if (F::digits <= T::digits) { - // From fits in To without any problem. - } else { - // From does not always fit in To, resort to a dynamic check. - if (from < T::min() || from > T::max()) { - // outside range. - ec = 1; - return {}; - } - } - return static_cast(from); -} - -/** - * converts From to To, without loss. If the dynamic value of from - * can't be converted to To without loss, ec is set. - */ -template ::value && - std::numeric_limits::is_signed != - std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - ec = 0; - using F = std::numeric_limits; - using T = std::numeric_limits; - static_assert(F::is_integer, "From must be integral"); - static_assert(T::is_integer, "To must be integral"); - - if (F::is_signed && !T::is_signed) { - // From may be negative, not allowed! - if (fmt::internal::is_negative(from)) { - ec = 1; - return {}; - } - - // From is positive. Can it always fit in To? - if (F::digits <= T::digits) { - // yes, From always fits in To. - } else { - // from may not fit in To, we have to do a dynamic check - if (from > static_cast(T::max())) { - ec = 1; - return {}; - } - } - } - - if (!F::is_signed && T::is_signed) { - // can from be held in To? - if (F::digits < T::digits) { - // yes, From always fits in To. - } else { - // from may not fit in To, we have to do a dynamic check - if (from > static_cast(T::max())) { - // outside range. - ec = 1; - return {}; - } - } - } - - // reaching here means all is ok for lossless conversion. - return static_cast(from); - -} // function - -template ::value)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { - ec = 0; - return from; -} // function - -// clang-format off -/** - * converts From to To if possible, otherwise ec is set. - * - * input | output - * ---------------------------------|--------------- - * NaN | NaN - * Inf | Inf - * normal, fits in output | converted (possibly lossy) - * normal, does not fit in output | ec is set - * subnormal | best effort - * -Inf | -Inf - */ -// clang-format on -template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { - ec = 0; - using T = std::numeric_limits; - static_assert(std::is_floating_point::value, "From must be floating"); - static_assert(std::is_floating_point::value, "To must be floating"); - - // catch the only happy case - if (std::isfinite(from)) { - if (from >= T::lowest() && from <= T::max()) { - return static_cast(from); - } - // not within range. - ec = 1; - return {}; - } - - // nan and inf will be preserved - return static_cast(from); -} // function - -template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { - ec = 0; - static_assert(std::is_floating_point::value, "From must be floating"); - return from; -} - -/** - * safe duration cast between integral durations - */ -template ::value), - FMT_ENABLE_IF(std::is_integral::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { - using From = std::chrono::duration; - ec = 0; - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // safe conversion to IntermediateRep - IntermediateRep count = - lossless_integral_conversion(from.count(), ec); - if (ec) { - return {}; - } - // multiply with Factor::num without overflow or underflow - if (Factor::num != 1) { - const auto max1 = internal::max_value() / Factor::num; - if (count > max1) { - ec = 1; - return {}; - } - const auto min1 = std::numeric_limits::min() / Factor::num; - if (count < min1) { - ec = 1; - return {}; - } - count *= Factor::num; - } - - // this can't go wrong, right? den>0 is checked earlier. - if (Factor::den != 1) { - count /= Factor::den; - } - // convert to the to type, safely - using ToRep = typename To::rep; - const ToRep tocount = lossless_integral_conversion(count, ec); - if (ec) { - return {}; - } - return To{tocount}; -} - -/** - * safe duration_cast between floating point durations - */ -template ::value), - FMT_ENABLE_IF(std::is_floating_point::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { - using From = std::chrono::duration; - ec = 0; - if (std::isnan(from.count())) { - // nan in, gives nan out. easy. - return To{std::numeric_limits::quiet_NaN()}; - } - // maybe we should also check if from is denormal, and decide what to do about - // it. - - // +-inf should be preserved. - if (std::isinf(from.count())) { - return To{from.count()}; - } - - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // force conversion of From::rep -> IntermediateRep to be safe, - // even if it will never happen be narrowing in this context. - IntermediateRep count = - safe_float_conversion(from.count(), ec); - if (ec) { - return {}; - } - - // multiply with Factor::num without overflow or underflow - if (Factor::num != 1) { - constexpr auto max1 = internal::max_value() / - static_cast(Factor::num); - if (count > max1) { - ec = 1; - return {}; - } - constexpr auto min1 = std::numeric_limits::lowest() / - static_cast(Factor::num); - if (count < min1) { - ec = 1; - return {}; - } - count *= static_cast(Factor::num); - } - - // this can't go wrong, right? den>0 is checked earlier. - if (Factor::den != 1) { - using common_t = typename std::common_type::type; - count /= static_cast(Factor::den); - } - - // convert to the to type, safely - using ToRep = typename To::rep; - - const ToRep tocount = safe_float_conversion(count, ec); - if (ec) { - return {}; - } - return To{tocount}; -} -} // namespace safe_duration_cast -#endif - -// Prevents expansion of a preceding token as a function-style macro. -// Usage: f FMT_NOMACRO() -#define FMT_NOMACRO - -namespace internal { -inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } -inline null<> localtime_s(...) { return null<>(); } -inline null<> gmtime_r(...) { return null<>(); } -inline null<> gmtime_s(...) { return null<>(); } -} // namespace internal - -// Thread-safe replacement for std::localtime -inline std::tm localtime(std::time_t time) { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - dispatcher(std::time_t t) : time_(t) {} - - bool run() { - using namespace fmt::internal; - return handle(localtime_r(&time_, &tm_)); - } - - bool handle(std::tm* tm) { return tm != nullptr; } - - bool handle(internal::null<>) { - using namespace fmt::internal; - return fallback(localtime_s(&tm_, &time_)); - } - - bool fallback(int res) { return res == 0; } - -#if !FMT_MSC_VER - bool fallback(internal::null<>) { - using namespace fmt::internal; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - dispatcher lt(time); - // Too big time values may be unsupported. - if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); - return lt.tm_; -} - -// Thread-safe replacement for std::gmtime -inline std::tm gmtime(std::time_t time) { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - dispatcher(std::time_t t) : time_(t) {} - - bool run() { - using namespace fmt::internal; - return handle(gmtime_r(&time_, &tm_)); - } - - bool handle(std::tm* tm) { return tm != nullptr; } - - bool handle(internal::null<>) { - using namespace fmt::internal; - return fallback(gmtime_s(&tm_, &time_)); - } - - bool fallback(int res) { return res == 0; } - -#if !FMT_MSC_VER - bool fallback(internal::null<>) { - std::tm* tm = std::gmtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - dispatcher gt(time); - // Too big time values may be unsupported. - if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); - return gt.tm_; -} - -namespace internal { -inline std::size_t strftime(char* str, std::size_t count, const char* format, - const std::tm* time) { - return std::strftime(str, count, format, time); -} - -inline std::size_t strftime(wchar_t* str, std::size_t count, - const wchar_t* format, const std::tm* time) { - return std::wcsftime(str, count, format, time); -} -} // namespace internal - -template struct formatter { - template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') ++it; - auto end = it; - while (end != ctx.end() && *end != '}') ++end; - tm_format.reserve(internal::to_unsigned(end - it + 1)); - tm_format.append(it, end); - tm_format.push_back('\0'); - return end; - } - - template - auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { - basic_memory_buffer buf; - std::size_t start = buf.size(); - for (;;) { - std::size_t size = buf.capacity() - start; - std::size_t count = - internal::strftime(&buf[start], size, &tm_format[0], &tm); - if (count != 0) { - buf.resize(start + count); - break; - } - if (size >= tm_format.size() * 256) { - // If the buffer is 256 times larger than the format string, assume - // that `strftime` gives an empty result. There doesn't seem to be a - // better way to distinguish the two cases: - // https://github.com/fmtlib/fmt/issues/367 - break; - } - const std::size_t MIN_GROWTH = 10; - buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); - } - return std::copy(buf.begin(), buf.end(), ctx.out()); - } - - basic_memory_buffer tm_format; -}; - -namespace internal { -template FMT_CONSTEXPR const char* get_units() { - return nullptr; -} -template <> FMT_CONSTEXPR const char* get_units() { return "as"; } -template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } -template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } -template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } -template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } -template <> FMT_CONSTEXPR const char* get_units() { return "das"; } -template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } -template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } -template <> FMT_CONSTEXPR const char* get_units>() { - return "m"; -} -template <> FMT_CONSTEXPR const char* get_units>() { - return "h"; -} - -enum class numeric_system { - standard, - // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. - alternative -}; - -// Parses a put_time-like format string and invokes handler actions. -template -FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - const Char* end, - Handler&& handler) { - auto ptr = begin; - while (ptr != end) { - auto c = *ptr; - if (c == '}') break; - if (c != '%') { - ++ptr; - continue; - } - if (begin != ptr) handler.on_text(begin, ptr); - ++ptr; // consume '%' - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case '%': - handler.on_text(ptr - 1, ptr); - break; - case 'n': { - const char newline[] = "\n"; - handler.on_text(newline, newline + 1); - break; - } - case 't': { - const char tab[] = "\t"; - handler.on_text(tab, tab + 1); - break; - } - // Day of the week: - case 'a': - handler.on_abbr_weekday(); - break; - case 'A': - handler.on_full_weekday(); - break; - case 'w': - handler.on_dec0_weekday(numeric_system::standard); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::standard); - break; - // Month: - case 'b': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; - // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard); - break; - case 'I': - handler.on_12_hour(numeric_system::standard); - break; - case 'M': - handler.on_minute(numeric_system::standard); - break; - case 'S': - handler.on_second(numeric_system::standard); - break; - // Other: - case 'c': - handler.on_datetime(numeric_system::standard); - break; - case 'x': - handler.on_loc_date(numeric_system::standard); - break; - case 'X': - handler.on_loc_time(numeric_system::standard); - break; - case 'D': - handler.on_us_date(); - break; - case 'F': - handler.on_iso_date(); - break; - case 'r': - handler.on_12_hour_time(); - break; - case 'R': - handler.on_24_hour_time(); - break; - case 'T': - handler.on_iso_time(); - break; - case 'p': - handler.on_am_pm(); - break; - case 'Q': - handler.on_duration_value(); - break; - case 'q': - handler.on_duration_unit(); - break; - case 'z': - handler.on_utc_offset(); - break; - case 'Z': - handler.on_tz_name(); - break; - // Alternative representation: - case 'E': { - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case 'c': - handler.on_datetime(numeric_system::alternative); - break; - case 'x': - handler.on_loc_date(numeric_system::alternative); - break; - case 'X': - handler.on_loc_time(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); - } - break; - } - case 'O': - if (ptr == end) FMT_THROW(format_error("invalid format")); - c = *ptr++; - switch (c) { - case 'w': - handler.on_dec0_weekday(numeric_system::alternative); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::alternative); - break; - case 'H': - handler.on_24_hour(numeric_system::alternative); - break; - case 'I': - handler.on_12_hour(numeric_system::alternative); - break; - case 'M': - handler.on_minute(numeric_system::alternative); - break; - case 'S': - handler.on_second(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); - } - break; - default: - FMT_THROW(format_error("invalid format")); - } - begin = ptr; - } - if (begin != ptr) handler.on_text(begin, ptr); - return ptr; -} - -struct chrono_format_checker { - FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } - - template void on_text(const Char*, const Char*) {} - FMT_NORETURN void on_abbr_weekday() { report_no_date(); } - FMT_NORETURN void on_full_weekday() { report_no_date(); } - FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } - FMT_NORETURN void on_abbr_month() { report_no_date(); } - FMT_NORETURN void on_full_month() { report_no_date(); } - void on_24_hour(numeric_system) {} - void on_12_hour(numeric_system) {} - void on_minute(numeric_system) {} - void on_second(numeric_system) {} - FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } - FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } - FMT_NORETURN void on_us_date() { report_no_date(); } - FMT_NORETURN void on_iso_date() { report_no_date(); } - void on_12_hour_time() {} - void on_24_hour_time() {} - void on_iso_time() {} - void on_am_pm() {} - void on_duration_value() {} - void on_duration_unit() {} - FMT_NORETURN void on_utc_offset() { report_no_date(); } - FMT_NORETURN void on_tz_name() { report_no_date(); } -}; - -template ::value)> -inline bool isnan(T) { - return false; -} -template ::value)> -inline bool isnan(T value) { - return std::isnan(value); -} - -template ::value)> -inline bool isfinite(T) { - return true; -} -template ::value)> -inline bool isfinite(T value) { - return std::isfinite(value); -} - -// Converts value to int and checks that it's in the range [0, upper). -template ::value)> -inline int to_nonnegative_int(T value, int upper) { - FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); - (void)upper; - return static_cast(value); -} -template ::value)> -inline int to_nonnegative_int(T value, int upper) { - FMT_ASSERT( - std::isnan(value) || (value >= 0 && value <= static_cast(upper)), - "invalid value"); - (void)upper; - return static_cast(value); -} - -template ::value)> -inline T mod(T x, int y) { - return x % static_cast(y); -} -template ::value)> -inline T mod(T x, int y) { - return std::fmod(x, static_cast(y)); -} - -// If T is an integral type, maps T to its unsigned counterpart, otherwise -// leaves it unchanged (unlike std::make_unsigned). -template ::value> -struct make_unsigned_or_unchanged { - using type = T; -}; - -template struct make_unsigned_or_unchanged { - using type = typename std::make_unsigned::type; -}; - -#if FMT_SAFE_DURATION_CAST -// throwing version of safe_duration_cast -template -To fmt_safe_duration_cast(std::chrono::duration from) { - int ec; - To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); - return to; -} -#endif - -template ::value)> -inline std::chrono::duration get_milliseconds( - std::chrono::duration d) { - // this may overflow and/or the result may not fit in the - // target type. -#if FMT_SAFE_DURATION_CAST - using CommonSecondsType = - typename std::common_type::type; - const auto d_as_common = fmt_safe_duration_cast(d); - const auto d_as_whole_seconds = - fmt_safe_duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = - fmt_safe_duration_cast>(diff); - return ms; -#else - auto s = std::chrono::duration_cast(d); - return std::chrono::duration_cast(d - s); -#endif -} - -template ::value)> -inline std::chrono::duration get_milliseconds( - std::chrono::duration d) { - using common_type = typename std::common_type::type; - auto ms = mod(d.count() * static_cast(Period::num) / - static_cast(Period::den) * 1000, - 1000); - return std::chrono::duration(static_cast(ms)); -} - -template -OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) { - if (precision >= 0) return format_to(out, "{:.{}f}", val, precision); - return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", - val); -} - -template -static OutputIt format_chrono_duration_unit(OutputIt out) { - if (const char* unit = get_units()) return format_to(out, "{}", unit); - if (Period::den == 1) return format_to(out, "[{}]s", Period::num); - return format_to(out, "[{}/{}]s", Period::num, Period::den); -} - -template -struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; - // rep is unsigned to avoid overflow. - using rep = - conditional_t::value && sizeof(Rep) < sizeof(int), - unsigned, typename make_unsigned_or_unchanged::type>; - rep val; - using seconds = std::chrono::duration; - seconds s; - using milliseconds = std::chrono::duration; - bool negative; - - using char_type = typename FormatContext::char_type; - - explicit chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), - negative(false) { - if (d.count() < 0) { - val = 0 - val; - negative = true; - } - - // this may overflow and/or the result may not fit in the - // target type. -#if FMT_SAFE_DURATION_CAST - // might need checked conversion (rep!=Rep) - auto tmpval = std::chrono::duration(val); - s = fmt_safe_duration_cast(tmpval); -#else - s = std::chrono::duration_cast( - std::chrono::duration(val)); -#endif - } - - // returns true if nan or inf, writes to out. - bool handle_nan_inf() { - if (isfinite(val)) { - return false; - } - if (isnan(val)) { - write_nan(); - return true; - } - // must be +-inf - if (val > 0) { - write_pinf(); - } else { - write_ninf(); - } - return true; - } - - Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } - - Rep hour12() const { - Rep hour = static_cast(mod((s.count() / 3600), 12)); - return hour <= 0 ? 12 : hour; - } - - Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } - Rep second() const { return static_cast(mod(s.count(), 60)); } - - std::tm time() const { - auto time = std::tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); - time.tm_min = to_nonnegative_int(minute(), 60); - time.tm_sec = to_nonnegative_int(second(), 60); - return time; - } - - void write_sign() { - if (negative) { - *out++ = '-'; - negative = false; - } - } - - void write(Rep value, int width) { - write_sign(); - if (isnan(value)) return write_nan(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(value, max_value())); - int num_digits = internal::count_digits(n); - if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); - out = format_decimal(out, n, num_digits); - } - - void write_nan() { std::copy_n("nan", 3, out); } - void write_pinf() { std::copy_n("inf", 3, out); } - void write_ninf() { std::copy_n("-inf", 4, out); } - - void format_localized(const tm& time, const char* format) { - if (isnan(val)) return write_nan(); - auto locale = context.locale().template get(); - auto& facet = std::use_facet>(locale); - std::basic_ostringstream os; - os.imbue(locale); - facet.put(os, os, ' ', &time, format, format + std::strlen(format)); - auto str = os.str(); - std::copy(str.begin(), str.end(), out); - } - - void on_text(const char_type* begin, const char_type* end) { - std::copy(begin, end, out); - } - - // These are not implemented because durations don't have date information. - void on_abbr_weekday() {} - void on_full_weekday() {} - void on_dec0_weekday(numeric_system) {} - void on_dec1_weekday(numeric_system) {} - void on_abbr_month() {} - void on_full_month() {} - void on_datetime(numeric_system) {} - void on_loc_date(numeric_system) {} - void on_loc_time(numeric_system) {} - void on_us_date() {} - void on_iso_date() {} - void on_utc_offset() {} - void on_tz_name() {} - - void on_24_hour(numeric_system ns) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(hour(), 2); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour(), 24); - format_localized(time, "%OH"); - } - - void on_12_hour(numeric_system ns) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(hour12(), 2); - auto time = tm(); - time.tm_hour = to_nonnegative_int(hour12(), 12); - format_localized(time, "%OI"); - } - - void on_minute(numeric_system ns) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) return write(minute(), 2); - auto time = tm(); - time.tm_min = to_nonnegative_int(minute(), 60); - format_localized(time, "%OM"); - } - - void on_second(numeric_system ns) { - if (handle_nan_inf()) return; - - if (ns == numeric_system::standard) { - write(second(), 2); -#if FMT_SAFE_DURATION_CAST - // convert rep->Rep - using duration_rep = std::chrono::duration; - using duration_Rep = std::chrono::duration; - auto tmpval = fmt_safe_duration_cast(duration_rep{val}); -#else - auto tmpval = std::chrono::duration(val); -#endif - auto ms = get_milliseconds(tmpval); - if (ms != std::chrono::milliseconds(0)) { - *out++ = '.'; - write(ms.count(), 3); - } - return; - } - auto time = tm(); - time.tm_sec = to_nonnegative_int(second(), 60); - format_localized(time, "%OS"); - } - - void on_12_hour_time() { - if (handle_nan_inf()) return; - - format_localized(time(), "%r"); - } - - void on_24_hour_time() { - if (handle_nan_inf()) { - *out++ = ':'; - handle_nan_inf(); - return; - } - - write(hour(), 2); - *out++ = ':'; - write(minute(), 2); - } - - void on_iso_time() { - on_24_hour_time(); - *out++ = ':'; - if (handle_nan_inf()) return; - write(second(), 2); - } - - void on_am_pm() { - if (handle_nan_inf()) return; - format_localized(time(), "%p"); - } - - void on_duration_value() { - if (handle_nan_inf()) return; - write_sign(); - out = format_chrono_duration_value(out, val, precision); - } - - void on_duration_unit() { out = format_chrono_duration_unit(out); } -}; -} // namespace internal - -template -struct formatter, Char> { - private: - basic_format_specs specs; - int precision; - using arg_ref_type = internal::arg_ref; - arg_ref_type width_ref; - arg_ref_type precision_ref; - mutable basic_string_view format_str; - using duration = std::chrono::duration; - - struct spec_handler { - formatter& f; - basic_format_parse_context& context; - basic_string_view format_str; - - template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { - context.check_arg_id(arg_id); - return arg_ref_type(arg_id); - } - - FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { - context.check_arg_id(arg_id); - return arg_ref_type(arg_id); - } - - FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { - return arg_ref_type(context.next_arg_id()); - } - - void on_error(const char* msg) { FMT_THROW(format_error(msg)); } - void on_fill(Char fill) { f.specs.fill[0] = fill; } - void on_align(align_t align) { f.specs.align = align; } - void on_width(int width) { f.specs.width = width; } - void on_precision(int _precision) { f.precision = _precision; } - void end_precision() {} - - template void on_dynamic_width(Id arg_id) { - f.width_ref = make_arg_ref(arg_id); - } - - template void on_dynamic_precision(Id arg_id) { - f.precision_ref = make_arg_ref(arg_id); - } - }; - - using iterator = typename basic_format_parse_context::iterator; - struct parse_range { - iterator begin; - iterator end; - }; - - FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin == end || *begin == '}') return {begin, begin}; - spec_handler handler{*this, ctx, format_str}; - begin = internal::parse_align(begin, end, handler); - if (begin == end) return {begin, begin}; - begin = internal::parse_width(begin, end, handler); - if (begin == end) return {begin, begin}; - if (*begin == '.') { - if (std::is_floating_point::value) - begin = internal::parse_precision(begin, end, handler); - else - handler.on_error("precision not allowed for this argument type"); - } - end = parse_chrono_format(begin, end, internal::chrono_format_checker()); - return {begin, end}; - } - - public: - formatter() : precision(-1) {} - - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto range = do_parse(ctx); - format_str = basic_string_view( - &*range.begin, internal::to_unsigned(range.end - range.begin)); - return range.end; - } - - template - auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { - auto begin = format_str.begin(), end = format_str.end(); - // As a possible future optimization, we could avoid extra copying if width - // is not specified. - basic_memory_buffer buf; - auto out = std::back_inserter(buf); - using range = internal::output_range; - internal::basic_writer w(range(ctx.out())); - internal::handle_dynamic_spec(specs.width, - width_ref, ctx); - internal::handle_dynamic_spec( - precision, precision_ref, ctx); - if (begin == end || *begin == '}') { - out = internal::format_chrono_duration_value(out, d.count(), precision); - internal::format_chrono_duration_unit(out); - } else { - internal::chrono_formatter f( - ctx, out, d); - f.precision = precision; - parse_chrono_format(begin, end, f); - } - w.write(buf.data(), buf.size(), specs); - return w.out(); - } -}; - -FMT_END_NAMESPACE - -#endif // FMT_CHRONO_H_ diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/color.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/color.h deleted file mode 100644 index 362a95e..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/color.h +++ /dev/null @@ -1,570 +0,0 @@ -// Formatting library for C++ - color support -// -// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_COLOR_H_ -#define FMT_COLOR_H_ - -#include "format.h" - -FMT_BEGIN_NAMESPACE - -enum class color : uint32_t { - alice_blue = 0xF0F8FF, // rgb(240,248,255) - antique_white = 0xFAEBD7, // rgb(250,235,215) - aqua = 0x00FFFF, // rgb(0,255,255) - aquamarine = 0x7FFFD4, // rgb(127,255,212) - azure = 0xF0FFFF, // rgb(240,255,255) - beige = 0xF5F5DC, // rgb(245,245,220) - bisque = 0xFFE4C4, // rgb(255,228,196) - black = 0x000000, // rgb(0,0,0) - blanched_almond = 0xFFEBCD, // rgb(255,235,205) - blue = 0x0000FF, // rgb(0,0,255) - blue_violet = 0x8A2BE2, // rgb(138,43,226) - brown = 0xA52A2A, // rgb(165,42,42) - burly_wood = 0xDEB887, // rgb(222,184,135) - cadet_blue = 0x5F9EA0, // rgb(95,158,160) - chartreuse = 0x7FFF00, // rgb(127,255,0) - chocolate = 0xD2691E, // rgb(210,105,30) - coral = 0xFF7F50, // rgb(255,127,80) - cornflower_blue = 0x6495ED, // rgb(100,149,237) - cornsilk = 0xFFF8DC, // rgb(255,248,220) - crimson = 0xDC143C, // rgb(220,20,60) - cyan = 0x00FFFF, // rgb(0,255,255) - dark_blue = 0x00008B, // rgb(0,0,139) - dark_cyan = 0x008B8B, // rgb(0,139,139) - dark_golden_rod = 0xB8860B, // rgb(184,134,11) - dark_gray = 0xA9A9A9, // rgb(169,169,169) - dark_green = 0x006400, // rgb(0,100,0) - dark_khaki = 0xBDB76B, // rgb(189,183,107) - dark_magenta = 0x8B008B, // rgb(139,0,139) - dark_olive_green = 0x556B2F, // rgb(85,107,47) - dark_orange = 0xFF8C00, // rgb(255,140,0) - dark_orchid = 0x9932CC, // rgb(153,50,204) - dark_red = 0x8B0000, // rgb(139,0,0) - dark_salmon = 0xE9967A, // rgb(233,150,122) - dark_sea_green = 0x8FBC8F, // rgb(143,188,143) - dark_slate_blue = 0x483D8B, // rgb(72,61,139) - dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) - dark_turquoise = 0x00CED1, // rgb(0,206,209) - dark_violet = 0x9400D3, // rgb(148,0,211) - deep_pink = 0xFF1493, // rgb(255,20,147) - deep_sky_blue = 0x00BFFF, // rgb(0,191,255) - dim_gray = 0x696969, // rgb(105,105,105) - dodger_blue = 0x1E90FF, // rgb(30,144,255) - fire_brick = 0xB22222, // rgb(178,34,34) - floral_white = 0xFFFAF0, // rgb(255,250,240) - forest_green = 0x228B22, // rgb(34,139,34) - fuchsia = 0xFF00FF, // rgb(255,0,255) - gainsboro = 0xDCDCDC, // rgb(220,220,220) - ghost_white = 0xF8F8FF, // rgb(248,248,255) - gold = 0xFFD700, // rgb(255,215,0) - golden_rod = 0xDAA520, // rgb(218,165,32) - gray = 0x808080, // rgb(128,128,128) - green = 0x008000, // rgb(0,128,0) - green_yellow = 0xADFF2F, // rgb(173,255,47) - honey_dew = 0xF0FFF0, // rgb(240,255,240) - hot_pink = 0xFF69B4, // rgb(255,105,180) - indian_red = 0xCD5C5C, // rgb(205,92,92) - indigo = 0x4B0082, // rgb(75,0,130) - ivory = 0xFFFFF0, // rgb(255,255,240) - khaki = 0xF0E68C, // rgb(240,230,140) - lavender = 0xE6E6FA, // rgb(230,230,250) - lavender_blush = 0xFFF0F5, // rgb(255,240,245) - lawn_green = 0x7CFC00, // rgb(124,252,0) - lemon_chiffon = 0xFFFACD, // rgb(255,250,205) - light_blue = 0xADD8E6, // rgb(173,216,230) - light_coral = 0xF08080, // rgb(240,128,128) - light_cyan = 0xE0FFFF, // rgb(224,255,255) - light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) - light_gray = 0xD3D3D3, // rgb(211,211,211) - light_green = 0x90EE90, // rgb(144,238,144) - light_pink = 0xFFB6C1, // rgb(255,182,193) - light_salmon = 0xFFA07A, // rgb(255,160,122) - light_sea_green = 0x20B2AA, // rgb(32,178,170) - light_sky_blue = 0x87CEFA, // rgb(135,206,250) - light_slate_gray = 0x778899, // rgb(119,136,153) - light_steel_blue = 0xB0C4DE, // rgb(176,196,222) - light_yellow = 0xFFFFE0, // rgb(255,255,224) - lime = 0x00FF00, // rgb(0,255,0) - lime_green = 0x32CD32, // rgb(50,205,50) - linen = 0xFAF0E6, // rgb(250,240,230) - magenta = 0xFF00FF, // rgb(255,0,255) - maroon = 0x800000, // rgb(128,0,0) - medium_aquamarine = 0x66CDAA, // rgb(102,205,170) - medium_blue = 0x0000CD, // rgb(0,0,205) - medium_orchid = 0xBA55D3, // rgb(186,85,211) - medium_purple = 0x9370DB, // rgb(147,112,219) - medium_sea_green = 0x3CB371, // rgb(60,179,113) - medium_slate_blue = 0x7B68EE, // rgb(123,104,238) - medium_spring_green = 0x00FA9A, // rgb(0,250,154) - medium_turquoise = 0x48D1CC, // rgb(72,209,204) - medium_violet_red = 0xC71585, // rgb(199,21,133) - midnight_blue = 0x191970, // rgb(25,25,112) - mint_cream = 0xF5FFFA, // rgb(245,255,250) - misty_rose = 0xFFE4E1, // rgb(255,228,225) - moccasin = 0xFFE4B5, // rgb(255,228,181) - navajo_white = 0xFFDEAD, // rgb(255,222,173) - navy = 0x000080, // rgb(0,0,128) - old_lace = 0xFDF5E6, // rgb(253,245,230) - olive = 0x808000, // rgb(128,128,0) - olive_drab = 0x6B8E23, // rgb(107,142,35) - orange = 0xFFA500, // rgb(255,165,0) - orange_red = 0xFF4500, // rgb(255,69,0) - orchid = 0xDA70D6, // rgb(218,112,214) - pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) - pale_green = 0x98FB98, // rgb(152,251,152) - pale_turquoise = 0xAFEEEE, // rgb(175,238,238) - pale_violet_red = 0xDB7093, // rgb(219,112,147) - papaya_whip = 0xFFEFD5, // rgb(255,239,213) - peach_puff = 0xFFDAB9, // rgb(255,218,185) - peru = 0xCD853F, // rgb(205,133,63) - pink = 0xFFC0CB, // rgb(255,192,203) - plum = 0xDDA0DD, // rgb(221,160,221) - powder_blue = 0xB0E0E6, // rgb(176,224,230) - purple = 0x800080, // rgb(128,0,128) - rebecca_purple = 0x663399, // rgb(102,51,153) - red = 0xFF0000, // rgb(255,0,0) - rosy_brown = 0xBC8F8F, // rgb(188,143,143) - royal_blue = 0x4169E1, // rgb(65,105,225) - saddle_brown = 0x8B4513, // rgb(139,69,19) - salmon = 0xFA8072, // rgb(250,128,114) - sandy_brown = 0xF4A460, // rgb(244,164,96) - sea_green = 0x2E8B57, // rgb(46,139,87) - sea_shell = 0xFFF5EE, // rgb(255,245,238) - sienna = 0xA0522D, // rgb(160,82,45) - silver = 0xC0C0C0, // rgb(192,192,192) - sky_blue = 0x87CEEB, // rgb(135,206,235) - slate_blue = 0x6A5ACD, // rgb(106,90,205) - slate_gray = 0x708090, // rgb(112,128,144) - snow = 0xFFFAFA, // rgb(255,250,250) - spring_green = 0x00FF7F, // rgb(0,255,127) - steel_blue = 0x4682B4, // rgb(70,130,180) - tan = 0xD2B48C, // rgb(210,180,140) - teal = 0x008080, // rgb(0,128,128) - thistle = 0xD8BFD8, // rgb(216,191,216) - tomato = 0xFF6347, // rgb(255,99,71) - turquoise = 0x40E0D0, // rgb(64,224,208) - violet = 0xEE82EE, // rgb(238,130,238) - wheat = 0xF5DEB3, // rgb(245,222,179) - white = 0xFFFFFF, // rgb(255,255,255) - white_smoke = 0xF5F5F5, // rgb(245,245,245) - yellow = 0xFFFF00, // rgb(255,255,0) - yellow_green = 0x9ACD32 // rgb(154,205,50) -}; // enum class color - -enum class terminal_color : uint8_t { - black = 30, - red, - green, - yellow, - blue, - magenta, - cyan, - white, - bright_black = 90, - bright_red, - bright_green, - bright_yellow, - bright_blue, - bright_magenta, - bright_cyan, - bright_white -}; - -enum class emphasis : uint8_t { - bold = 1, - italic = 1 << 1, - underline = 1 << 2, - strikethrough = 1 << 3 -}; - -// rgb is a struct for red, green and blue colors. -// Using the name "rgb" makes some editors show the color in a tooltip. -struct rgb { - FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} - FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} - FMT_CONSTEXPR rgb(uint32_t hex) - : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} - FMT_CONSTEXPR rgb(color hex) - : r((uint32_t(hex) >> 16) & 0xFF), - g((uint32_t(hex) >> 8) & 0xFF), - b(uint32_t(hex) & 0xFF) {} - uint8_t r; - uint8_t g; - uint8_t b; -}; - -namespace internal { - -// color is a struct of either a rgb color or a terminal color. -struct color_type { - FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), - value{} { - value.rgb_color = static_cast(rgb_color); - } - FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { - value.rgb_color = (static_cast(rgb_color.r) << 16) | - (static_cast(rgb_color.g) << 8) | rgb_color.b; - } - FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), - value{} { - value.term_color = static_cast(term_color); - } - bool is_rgb; - union color_union { - uint8_t term_color; - uint32_t rgb_color; - } value; -}; -} // namespace internal - -// Experimental text formatting support. -class text_style { - public: - FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT - : set_foreground_color(), - set_background_color(), - ems(em) {} - - FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - FMT_THROW(format_error("can't OR a terminal color")); - foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; - } - - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - FMT_THROW(format_error("can't OR a terminal color")); - background_color.value.rgb_color |= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) | - static_cast(rhs.ems)); - return *this; - } - - friend FMT_CONSTEXPR text_style operator|(text_style lhs, - const text_style& rhs) { - return lhs |= rhs; - } - - FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; - } - - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - background_color.value.rgb_color &= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) & - static_cast(rhs.ems)); - return *this; - } - - friend FMT_CONSTEXPR text_style operator&(text_style lhs, - const text_style& rhs) { - return lhs &= rhs; - } - - FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { - return set_foreground_color; - } - FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { - return set_background_color; - } - FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { - return static_cast(ems) != 0; - } - FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { - FMT_ASSERT(has_foreground(), "no foreground specified for this style"); - return foreground_color; - } - FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { - FMT_ASSERT(has_background(), "no background specified for this style"); - return background_color; - } - FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { - FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); - return ems; - } - - private: - FMT_CONSTEXPR text_style(bool is_foreground, - internal::color_type text_color) FMT_NOEXCEPT - : set_foreground_color(), - set_background_color(), - ems() { - if (is_foreground) { - foreground_color = text_color; - set_foreground_color = true; - } else { - background_color = text_color; - set_background_color = true; - } - } - - friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) - FMT_NOEXCEPT; - friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background) - FMT_NOEXCEPT; - - internal::color_type foreground_color; - internal::color_type background_color; - bool set_foreground_color; - bool set_background_color; - emphasis ems; -}; - -FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT { - return text_style(/*is_foreground=*/true, foreground); -} - -FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT { - return text_style(/*is_foreground=*/false, background); -} - -FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { - return text_style(lhs) | rhs; -} - -namespace internal { - -template struct ansi_color_escape { - FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, - const char* esc) FMT_NOEXCEPT { - // If we have a terminal color, we need to output another escape code - // sequence. - if (!text_color.is_rgb) { - bool is_background = esc == internal::data::background_color; - uint32_t value = text_color.value.term_color; - // Background ASCII codes are the same as the foreground ones but with - // 10 more. - if (is_background) value += 10u; - - std::size_t index = 0; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); - - if (value >= 100u) { - buffer[index++] = static_cast('1'); - value %= 100u; - } - buffer[index++] = static_cast('0' + value / 10u); - buffer[index++] = static_cast('0' + value % 10u); - - buffer[index++] = static_cast('m'); - buffer[index++] = static_cast('\0'); - return; - } - - for (int i = 0; i < 7; i++) { - buffer[i] = static_cast(esc[i]); - } - rgb color(text_color.value.rgb_color); - to_esc(color.r, buffer + 7, ';'); - to_esc(color.g, buffer + 11, ';'); - to_esc(color.b, buffer + 15, 'm'); - buffer[19] = static_cast(0); - } - FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { - uint8_t em_codes[4] = {}; - uint8_t em_bits = static_cast(em); - if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; - if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; - if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; - if (em_bits & static_cast(emphasis::strikethrough)) - em_codes[3] = 9; - - std::size_t index = 0; - for (int i = 0; i < 4; ++i) { - if (!em_codes[i]) continue; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); - buffer[index++] = static_cast('0' + em_codes[i]); - buffer[index++] = static_cast('m'); - } - buffer[index++] = static_cast(0); - } - FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } - - FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } - FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { - return buffer + std::strlen(buffer); - } - - private: - Char buffer[7u + 3u * 4u + 1u]; - - static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, - char delimiter) FMT_NOEXCEPT { - out[0] = static_cast('0' + c / 100); - out[1] = static_cast('0' + c / 10 % 10); - out[2] = static_cast('0' + c % 10); - out[3] = static_cast(delimiter); - } -}; - -template -FMT_CONSTEXPR ansi_color_escape make_foreground_color( - internal::color_type foreground) FMT_NOEXCEPT { - return ansi_color_escape(foreground, internal::data::foreground_color); -} - -template -FMT_CONSTEXPR ansi_color_escape make_background_color( - internal::color_type background) FMT_NOEXCEPT { - return ansi_color_escape(background, internal::data::background_color); -} - -template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { - return ansi_color_escape(em); -} - -template -inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { - std::fputs(chars, stream); -} - -template <> -inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { - std::fputws(chars, stream); -} - -template inline void reset_color(FILE* stream) FMT_NOEXCEPT { - fputs(internal::data::reset_color, stream); -} - -template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { - fputs(internal::data::wreset_color, stream); -} - -template -inline void reset_color(basic_memory_buffer& buffer) FMT_NOEXCEPT { - const char* begin = data::reset_color; - const char* end = begin + sizeof(data::reset_color) - 1; - buffer.append(begin, end); -} - -template -void vformat_to(basic_memory_buffer& buf, const text_style& ts, - basic_string_view format_str, - basic_format_args> args) { - bool has_style = false; - if (ts.has_emphasis()) { - has_style = true; - auto emphasis = internal::make_emphasis(ts.get_emphasis()); - buf.append(emphasis.begin(), emphasis.end()); - } - if (ts.has_foreground()) { - has_style = true; - auto foreground = - internal::make_foreground_color(ts.get_foreground()); - buf.append(foreground.begin(), foreground.end()); - } - if (ts.has_background()) { - has_style = true; - auto background = - internal::make_background_color(ts.get_background()); - buf.append(background.begin(), background.end()); - } - vformat_to(buf, format_str, args); - if (has_style) { - internal::reset_color(buf); - } -} -} // namespace internal - -template > -void vprint(std::FILE* f, const text_style& ts, const S& format, - basic_format_args> args) { - basic_memory_buffer buf; - internal::vformat_to(buf, ts, to_string_view(format), args); - buf.push_back(Char(0)); - internal::fputs(buf.data(), f); -} - -/** - Formats a string and prints it to the specified file stream using ANSI - escape sequences to specify text formatting. - Example: - fmt::print(fmt::emphasis::bold | fg(fmt::color::red), - "Elapsed time: {0:.2f} seconds", 1.23); - */ -template ::value)> -void print(std::FILE* f, const text_style& ts, const S& format_str, - const Args&... args) { - internal::check_format_string(format_str); - using context = buffer_context>; - format_arg_store as{args...}; - vprint(f, ts, format_str, basic_format_args(as)); -} - -/** - Formats a string and prints it to stdout using ANSI escape sequences to - specify text formatting. - Example: - fmt::print(fmt::emphasis::bold | fg(fmt::color::red), - "Elapsed time: {0:.2f} seconds", 1.23); - */ -template ::value)> -void print(const text_style& ts, const S& format_str, const Args&... args) { - return print(stdout, ts, format_str, args...); -} - -template > -inline std::basic_string vformat( - const text_style& ts, const S& format_str, - basic_format_args> args) { - basic_memory_buffer buf; - internal::vformat_to(buf, ts, to_string_view(format_str), args); - return fmt::to_string(buf); -} - -/** - \rst - Formats arguments and returns the result as a string using ANSI - escape sequences to specify text formatting. - - **Example**:: - - #include - std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), - "The answer is {}", 42); - \endrst -*/ -template > -inline std::basic_string format(const text_style& ts, const S& format_str, - const Args&... args) { - return vformat(ts, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); -} - -FMT_END_NAMESPACE - -#endif // FMT_COLOR_H_ diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/compile.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/compile.h deleted file mode 100644 index 5829f62..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/compile.h +++ /dev/null @@ -1,585 +0,0 @@ -// Formatting library for C++ - experimental format string compilation -// -// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_COMPILE_H_ -#define FMT_COMPILE_H_ - -#include -#include "format.h" - -FMT_BEGIN_NAMESPACE -namespace internal { - -// Part of a compiled format string. It can be either literal text or a -// replacement field. -template struct format_part { - enum class kind { arg_index, arg_name, text, replacement }; - - struct replacement { - arg_ref arg_id; - dynamic_format_specs specs; - }; - - kind part_kind; - union value { - int arg_index; - basic_string_view str; - replacement repl; - - FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} - FMT_CONSTEXPR value(basic_string_view s) : str(s) {} - FMT_CONSTEXPR value(replacement r) : repl(r) {} - } val; - // Position past the end of the argument id. - const Char* arg_id_end = nullptr; - - FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) - : part_kind(k), val(v) {} - - static FMT_CONSTEXPR format_part make_arg_index(int index) { - return format_part(kind::arg_index, index); - } - static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { - return format_part(kind::arg_name, name); - } - static FMT_CONSTEXPR format_part make_text(basic_string_view text) { - return format_part(kind::text, text); - } - static FMT_CONSTEXPR format_part make_replacement(replacement repl) { - return format_part(kind::replacement, repl); - } -}; - -template struct part_counter { - unsigned num_parts = 0; - - FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - if (begin != end) ++num_parts; - } - - FMT_CONSTEXPR void on_arg_id() { ++num_parts; } - FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; } - FMT_CONSTEXPR void on_arg_id(basic_string_view) { ++num_parts; } - - FMT_CONSTEXPR void on_replacement_field(const Char*) {} - - FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, - const Char* end) { - // Find the matching brace. - unsigned brace_counter = 0; - for (; begin != end; ++begin) { - if (*begin == '{') { - ++brace_counter; - } else if (*begin == '}') { - if (brace_counter == 0u) break; - --brace_counter; - } - } - return begin; - } - - FMT_CONSTEXPR void on_error(const char*) {} -}; - -// Counts the number of parts in a format string. -template -FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { - part_counter counter; - parse_format_string(format_str, counter); - return counter.num_parts; -} - -template -class format_string_compiler : public error_handler { - private: - using part = format_part; - - PartHandler handler_; - part part_; - basic_string_view format_str_; - basic_format_parse_context parse_context_; - - public: - FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, - PartHandler handler) - : handler_(handler), - format_str_(format_str), - parse_context_(format_str) {} - - FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - if (begin != end) - handler_(part::make_text({begin, to_unsigned(end - begin)})); - } - - FMT_CONSTEXPR void on_arg_id() { - part_ = part::make_arg_index(parse_context_.next_arg_id()); - } - - FMT_CONSTEXPR void on_arg_id(int id) { - parse_context_.check_arg_id(id); - part_ = part::make_arg_index(id); - } - - FMT_CONSTEXPR void on_arg_id(basic_string_view id) { - part_ = part::make_arg_name(id); - } - - FMT_CONSTEXPR void on_replacement_field(const Char* ptr) { - part_.arg_id_end = ptr; - handler_(part_); - } - - FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, - const Char* end) { - auto repl = typename part::replacement(); - dynamic_specs_handler> handler( - repl.specs, parse_context_); - auto it = parse_format_specs(begin, end, handler); - if (*it != '}') on_error("missing '}' in format string"); - repl.arg_id = part_.part_kind == part::kind::arg_index - ? arg_ref(part_.val.arg_index) - : arg_ref(part_.val.str); - auto part = part::make_replacement(repl); - part.arg_id_end = begin; - handler_(part); - return it; - } -}; - -// Compiles a format string and invokes handler(part) for each parsed part. -template -FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, - PartHandler handler) { - parse_format_string( - format_str, - format_string_compiler(format_str, handler)); -} - -template -void format_arg( - basic_format_parse_context& parse_ctx, - Context& ctx, Id arg_id) { - ctx.advance_to( - visit_format_arg(arg_formatter(ctx, &parse_ctx), ctx.arg(arg_id))); -} - -// vformat_to is defined in a subnamespace to prevent ADL. -namespace cf { -template -auto vformat_to(Range out, CompiledFormat& cf, basic_format_args args) - -> typename Context::iterator { - using char_type = typename Context::char_type; - basic_format_parse_context parse_ctx( - to_string_view(cf.format_str_)); - Context ctx(out.begin(), args); - - const auto& parts = cf.parts(); - for (auto part_it = std::begin(parts); part_it != std::end(parts); - ++part_it) { - const auto& part = *part_it; - const auto& value = part.val; - - using format_part_t = format_part; - switch (part.part_kind) { - case format_part_t::kind::text: { - const auto text = value.str; - auto output = ctx.out(); - auto&& it = reserve(output, text.size()); - it = std::copy_n(text.begin(), text.size(), it); - ctx.advance_to(output); - break; - } - - case format_part_t::kind::arg_index: - advance_to(parse_ctx, part.arg_id_end); - internal::format_arg(parse_ctx, ctx, value.arg_index); - break; - - case format_part_t::kind::arg_name: - advance_to(parse_ctx, part.arg_id_end); - internal::format_arg(parse_ctx, ctx, value.str); - break; - - case format_part_t::kind::replacement: { - const auto& arg_id_value = value.repl.arg_id.val; - const auto arg = value.repl.arg_id.kind == arg_id_kind::index - ? ctx.arg(arg_id_value.index) - : ctx.arg(arg_id_value.name); - - auto specs = value.repl.specs; - - handle_dynamic_spec(specs.width, specs.width_ref, ctx); - handle_dynamic_spec(specs.precision, - specs.precision_ref, ctx); - - error_handler h; - numeric_specs_checker checker(h, arg.type()); - if (specs.align == align::numeric) checker.require_numeric_argument(); - if (specs.sign != sign::none) checker.check_sign(); - if (specs.alt) checker.require_numeric_argument(); - if (specs.precision >= 0) checker.check_precision(); - - advance_to(parse_ctx, part.arg_id_end); - ctx.advance_to( - visit_format_arg(arg_formatter(ctx, nullptr, &specs), arg)); - break; - } - } - } - return ctx.out(); -} -} // namespace cf - -struct basic_compiled_format {}; - -template -struct compiled_format_base : basic_compiled_format { - using char_type = char_t; - using parts_container = std::vector>; - - parts_container compiled_parts; - - explicit compiled_format_base(basic_string_view format_str) { - compile_format_string(format_str, - [this](const format_part& part) { - compiled_parts.push_back(part); - }); - } - - const parts_container& parts() const { return compiled_parts; } -}; - -template struct format_part_array { - format_part data[N] = {}; - FMT_CONSTEXPR format_part_array() = default; -}; - -template -FMT_CONSTEXPR format_part_array compile_to_parts( - basic_string_view format_str) { - format_part_array parts; - unsigned counter = 0; - // This is not a lambda for compatibility with older compilers. - struct { - format_part* parts; - unsigned* counter; - FMT_CONSTEXPR void operator()(const format_part& part) { - parts[(*counter)++] = part; - } - } collector{parts.data, &counter}; - compile_format_string(format_str, collector); - if (counter < N) { - parts.data[counter] = - format_part::make_text(basic_string_view()); - } - return parts; -} - -template constexpr const T& constexpr_max(const T& a, const T& b) { - return (a < b) ? b : a; -} - -template -struct compiled_format_base::value>> - : basic_compiled_format { - using char_type = char_t; - - FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} - -// Workaround for old compilers. Format string compilation will not be -// performed there anyway. -#if FMT_USE_CONSTEXPR - static FMT_CONSTEXPR_DECL const unsigned num_format_parts = - constexpr_max(count_parts(to_string_view(S())), 1u); -#else - static const unsigned num_format_parts = 1; -#endif - - using parts_container = format_part[num_format_parts]; - - const parts_container& parts() const { - static FMT_CONSTEXPR_DECL const auto compiled_parts = - compile_to_parts( - internal::to_string_view(S())); - return compiled_parts.data; - } -}; - -template -class compiled_format : private compiled_format_base { - public: - using typename compiled_format_base::char_type; - - private: - basic_string_view format_str_; - - template - friend auto cf::vformat_to(Range out, CompiledFormat& cf, - basic_format_args args) -> - typename Context::iterator; - - public: - compiled_format() = delete; - explicit constexpr compiled_format(basic_string_view format_str) - : compiled_format_base(format_str), format_str_(format_str) {} -}; - -#ifdef __cpp_if_constexpr -template struct type_list {}; - -// Returns a reference to the argument at index N from [first, rest...]. -template -constexpr const auto& get(const T& first, const Args&... rest) { - static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); - if constexpr (N == 0) - return first; - else - return get(rest...); -} - -template struct get_type_impl; - -template struct get_type_impl> { - using type = remove_cvref_t(std::declval()...))>; -}; - -template -using get_type = typename get_type_impl::type; - -template struct text { - basic_string_view data; - using char_type = Char; - - template - OutputIt format(OutputIt out, const Args&...) const { - // TODO: reserve - return copy_str(data.begin(), data.end(), out); - } -}; - -template -constexpr text make_text(basic_string_view s, size_t pos, - size_t size) { - return {{&s[pos], size}}; -} - -template , int> = 0> -OutputIt format_default(OutputIt out, T value) { - // TODO: reserve - format_int fi(value); - return std::copy(fi.data(), fi.data() + fi.size(), out); -} - -template -OutputIt format_default(OutputIt out, double value) { - writer w(out); - w.write(value); - return w.out(); -} - -template -OutputIt format_default(OutputIt out, Char value) { - *out++ = value; - return out; -} - -template -OutputIt format_default(OutputIt out, const Char* value) { - auto length = std::char_traits::length(value); - return copy_str(value, value + length, out); -} - -// A replacement field that refers to argument N. -template struct field { - using char_type = Char; - - template - OutputIt format(OutputIt out, const Args&... args) const { - // This ensures that the argument type is convertile to `const T&`. - const T& arg = get(args...); - return format_default(out, arg); - } -}; - -template struct concat { - L lhs; - R rhs; - using char_type = typename L::char_type; - - template - OutputIt format(OutputIt out, const Args&... args) const { - out = lhs.format(out, args...); - return rhs.format(out, args...); - } -}; - -template -constexpr concat make_concat(L lhs, R rhs) { - return {lhs, rhs}; -} - -struct unknown_format {}; - -template -constexpr size_t parse_text(basic_string_view str, size_t pos) { - for (size_t size = str.size(); pos != size; ++pos) { - if (str[pos] == '{' || str[pos] == '}') break; - } - return pos; -} - -template -constexpr auto compile_format_string(S format_str); - -template -constexpr auto parse_tail(T head, S format_str) { - if constexpr (POS != to_string_view(format_str).size()) { - constexpr auto tail = compile_format_string(format_str); - if constexpr (std::is_same, - unknown_format>()) - return tail; - else - return make_concat(head, tail); - } else { - return head; - } -} - -// Compiles a non-empty format string and returns the compiled representation -// or unknown_format() on unrecognized input. -template -constexpr auto compile_format_string(S format_str) { - using char_type = typename S::char_type; - constexpr basic_string_view str = format_str; - if constexpr (str[POS] == '{') { - if (POS + 1 == str.size()) - throw format_error("unmatched '{' in format string"); - if constexpr (str[POS + 1] == '{') { - return parse_tail(make_text(str, POS, 1), format_str); - } else if constexpr (str[POS + 1] == '}') { - using type = get_type; - if constexpr (std::is_same::value) { - return parse_tail(field(), - format_str); - } else { - return unknown_format(); - } - } else { - return unknown_format(); - } - } else if constexpr (str[POS] == '}') { - if (POS + 1 == str.size()) - throw format_error("unmatched '}' in format string"); - return parse_tail(make_text(str, POS, 1), format_str); - } else { - constexpr auto end = parse_text(str, POS + 1); - return parse_tail(make_text(str, POS, end - POS), - format_str); - } -} -#endif // __cpp_if_constexpr -} // namespace internal - -#if FMT_USE_CONSTEXPR -# ifdef __cpp_if_constexpr -template ::value)> -constexpr auto compile(S format_str) { - constexpr basic_string_view str = format_str; - if constexpr (str.size() == 0) { - return internal::make_text(str, 0, 0); - } else { - constexpr auto result = - internal::compile_format_string, 0, 0>( - format_str); - if constexpr (std::is_same, - internal::unknown_format>()) { - return internal::compiled_format(to_string_view(format_str)); - } else { - return result; - } - } -} - -template ::value)> -std::basic_string format(const CompiledFormat& cf, const Args&... args) { - basic_memory_buffer buffer; - cf.format(std::back_inserter(buffer), args...); - return to_string(buffer); -} - -template ::value)> -OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { - return cf.format(out, args...); -} -# else -template ::value)> -constexpr auto compile(S format_str) -> internal::compiled_format { - return internal::compiled_format(to_string_view(format_str)); -} -# endif // __cpp_if_constexpr -#endif // FMT_USE_CONSTEXPR - -// Compiles the format string which must be a string literal. -template -auto compile(const Char (&format_str)[N]) - -> internal::compiled_format { - return internal::compiled_format( - basic_string_view(format_str, N - 1)); -} - -template ::value)> -std::basic_string format(const CompiledFormat& cf, const Args&... args) { - basic_memory_buffer buffer; - using range = buffer_range; - using context = buffer_context; - internal::cf::vformat_to(range(buffer), cf, - {make_format_args(args...)}); - return to_string(buffer); -} - -template ::value)> -OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { - using char_type = typename CompiledFormat::char_type; - using range = internal::output_range; - using context = format_context_t; - return internal::cf::vformat_to( - range(out), cf, {make_format_args(args...)}); -} - -template ::value)> -format_to_n_result format_to_n(OutputIt out, size_t n, - const CompiledFormat& cf, - const Args&... args) { - auto it = - format_to(internal::truncating_iterator(out, n), cf, args...); - return {it.base(), it.count()}; -} - -template -std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { - return format_to(internal::counting_iterator(), cf, args...).count(); -} - -FMT_END_NAMESPACE - -#endif // FMT_COMPILE_H_ diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/core.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/core.h deleted file mode 100644 index 9fd8df2..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/core.h +++ /dev/null @@ -1,1519 +0,0 @@ -// Formatting library for C++ - the core API -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_CORE_H_ -#define FMT_CORE_H_ - -#include // std::FILE -#include -#include -#include -#include - -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 60102 - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#if defined(__has_include) && !defined(__INTELLISENSE__) && \ - !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600) -# define FMT_HAS_INCLUDE(x) __has_include(x) -#else -# define FMT_HAS_INCLUDE(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#else -# define FMT_GCC_VERSION 0 -#endif - -#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) -# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION -#else -# define FMT_HAS_GXX_CXX11 0 -#endif - -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ -#else -# define FMT_NVCC 0 -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -#else -# define FMT_MSC_VER 0 -#endif - -// Check if relaxed C++14 constexpr is supported. -// GCC doesn't allow throw in constexpr until version 6 (bug 67371). -#ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC -#endif -#if FMT_USE_CONSTEXPR -# define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr -#else -# define FMT_CONSTEXPR inline -# define FMT_CONSTEXPR_DECL -#endif - -#ifndef FMT_OVERRIDE -# if FMT_HAS_FEATURE(cxx_override) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_OVERRIDE override -# else -# define FMT_OVERRIDE -# endif -#endif - -// Check if exceptions are disabled. -#ifndef FMT_EXCEPTIONS -# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -# else -# define FMT_EXCEPTIONS 1 -# endif -#endif - -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept -# define FMT_HAS_CXX11_NOEXCEPT 1 -#else -# define FMT_DETECTED_NOEXCEPT throw() -# define FMT_HAS_CXX11_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT -# else -# define FMT_NOEXCEPT -# endif -#endif - -// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER -# define FMT_NORETURN [[noreturn]] -#else -# define FMT_NORETURN -#endif - -#ifndef FMT_DEPRECATED -# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \ - FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if defined(__GNUC__) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - -// Workaround broken [[deprecated]] in the Intel compiler and NVCC. -#if defined(__INTEL_COMPILER) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -#endif - -#ifndef FMT_BEGIN_NAMESPACE -# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ - FMT_MSC_VER >= 1900 -# define FMT_INLINE_NAMESPACE inline namespace -# define FMT_END_NAMESPACE \ - } \ - } -# else -# define FMT_INLINE_NAMESPACE namespace -# define FMT_END_NAMESPACE \ - } \ - using namespace v6; \ - } -# endif -# define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ - FMT_INLINE_NAMESPACE v6 { -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# define FMT_EXTERN_TEMPLATE_API FMT_API -# endif -#endif -#ifndef FMT_API -# define FMT_API -#endif -#ifndef FMT_EXTERN_TEMPLATE_API -# define FMT_EXTERN_TEMPLATE_API -#endif - -#ifndef FMT_HEADER_ONLY -# define FMT_EXTERN extern -#else -# define FMT_EXTERN -#endif - -// libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) -# include -# define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW -#endif - -FMT_BEGIN_NAMESPACE - -// Implementations of enable_if_t and other types for pre-C++14 systems. -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; -template using bool_constant = std::integral_constant; -template -using remove_reference_t = typename std::remove_reference::type; -template -using remove_const_t = typename std::remove_const::type; -template -using remove_cvref_t = typename std::remove_cv>::type; - -struct monostate {}; - -// An enable_if helper to be used in template parameters which results in much -// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed -// to workaround a bug in MSVC 2019 (see #1140 and #1186). -#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 - -namespace internal { - -// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; - -FMT_API void assert_fail(const char* file, int line, const char* message); - -#ifndef FMT_ASSERT -# ifdef NDEBUG -# define FMT_ASSERT(condition, message) -# else -# define FMT_ASSERT(condition, message) \ - ((condition) \ - ? void() \ - : fmt::internal::assert_fail(__FILE__, __LINE__, (message))) -# endif -#endif - -#if defined(FMT_USE_STRING_VIEW) -template using std_string_view = std::basic_string_view; -#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) -template -using std_string_view = std::experimental::basic_string_view; -#else -template struct std_string_view {}; -#endif - -#ifdef FMT_USE_INT128 -// Do nothing. -#elif defined(__SIZEOF_INT128__) -# define FMT_USE_INT128 1 -using int128_t = __int128_t; -using uint128_t = __uint128_t; -#else -# define FMT_USE_INT128 0 -#endif -#if !FMT_USE_INT128 -struct int128_t {}; -struct uint128_t {}; -#endif - -// Casts a nonnegative integer to unsigned. -template -FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { - FMT_ASSERT(value >= 0, "negative value"); - return static_cast::type>(value); -} -} // namespace internal - -template -using void_t = typename internal::void_t_impl::type; - -/** - An implementation of ``std::basic_string_view`` for pre-C++17. It provides a - subset of the API. ``fmt::basic_string_view`` is used for format strings even - if ``std::string_view`` is available to prevent issues when a library is - compiled with a different ``-std`` option than the client code (which is not - recommended). - */ -template class basic_string_view { - private: - const Char* data_; - size_t size_; - - public: - using char_type = Char; - using iterator = const Char*; - - FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} - - /** Constructs a string reference object from a C string and a size. */ - FMT_CONSTEXPR basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT - : data_(s), - size_(count) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - basic_string_view(const Char* s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** Constructs a string reference from a ``std::basic_string`` object. */ - template - FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) FMT_NOEXCEPT - : data_(s.data()), - size_(s.size()) {} - - template < - typename S, - FMT_ENABLE_IF(std::is_same>::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} - - /** Returns a pointer to the string data. */ - FMT_CONSTEXPR const Char* data() const { return data_; } - - /** Returns the string size. */ - FMT_CONSTEXPR size_t size() const { return size_; } - - FMT_CONSTEXPR iterator begin() const { return data_; } - FMT_CONSTEXPR iterator end() const { return data_ + size_; } - - FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; } - - FMT_CONSTEXPR void remove_prefix(size_t n) { - data_ += n; - size_ -= n; - } - - // Lexicographically compare this string reference to other. - int compare(basic_string_view other) const { - size_t str_size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, str_size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; - } - - friend bool operator==(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) == 0; - } - friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) != 0; - } - friend bool operator<(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) < 0; - } - friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) <= 0; - } - friend bool operator>(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) > 0; - } - friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { - return lhs.compare(rhs) >= 0; - } -}; - -using string_view = basic_string_view; -using wstring_view = basic_string_view; - -#ifndef __cpp_char8_t -// A UTF-8 code unit type. -enum char8_t : unsigned char {}; -#endif - -/** Specifies if ``T`` is a character type. Can be specialized by users. */ -template struct is_char : std::false_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; - -/** - \rst - Returns a string view of `s`. In order to add custom string type support to - {fmt} provide an overload of `to_string_view` for it in the same namespace as - the type for the argument-dependent lookup to work. - - **Example**:: - - namespace my_ns { - inline string_view to_string_view(const my_string& s) { - return {s.data(), s.length()}; - } - } - std::string message = fmt::format(my_string("The answer is {}"), 42); - \endrst - */ -template ::value)> -inline basic_string_view to_string_view(const Char* s) { - return s; -} - -template -inline basic_string_view to_string_view( - const std::basic_string& s) { - return s; -} - -template -inline basic_string_view to_string_view(basic_string_view s) { - return s; -} - -template >::value)> -inline basic_string_view to_string_view( - internal::std_string_view s) { - return s; -} - -// A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - -template ::value)> -constexpr basic_string_view to_string_view(const S& s) { - return s; -} - -namespace internal { -void to_string_view(...); -using fmt::v6::to_string_view; - -// Specifies whether S is a string type convertible to fmt::basic_string_view. -// It should be a constexpr function but MSVC 2017 fails to compile it in -// enable_if and MSVC 2015 fails to compile it as an alias template. -template -struct is_string : std::is_class()))> { -}; - -template struct char_t_impl {}; -template struct char_t_impl::value>> { - using result = decltype(to_string_view(std::declval())); - using type = typename result::char_type; -}; - -struct error_handler { - FMT_CONSTEXPR error_handler() = default; - FMT_CONSTEXPR error_handler(const error_handler&) = default; - - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); -}; -} // namespace internal - -/** String's character type. */ -template using char_t = typename internal::char_t_impl::type; - -/** - \rst - Parsing context consisting of a format string range being parsed and an - argument counter for automatic indexing. - - You can use one of the following type aliases for common character types: - - +-----------------------+-------------------------------------+ - | Type | Definition | - +=======================+=====================================+ - | format_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ - | wformat_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ - \endrst - */ -template -class basic_format_parse_context : private ErrorHandler { - private: - basic_string_view format_str_; - int next_arg_id_; - - public: - using char_type = Char; - using iterator = typename basic_string_view::iterator; - - explicit FMT_CONSTEXPR basic_format_parse_context( - basic_string_view format_str, ErrorHandler eh = ErrorHandler()) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} - - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ - FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { - return format_str_.begin(); - } - - /** - Returns an iterator past the end of the format string range being parsed. - */ - FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } - - /** Advances the begin iterator to ``it``. */ - FMT_CONSTEXPR void advance_to(iterator it) { - format_str_.remove_prefix(internal::to_unsigned(it - begin())); - } - - /** - Reports an error if using the manual argument indexing; otherwise returns - the next argument index and switches to the automatic indexing. - */ - FMT_CONSTEXPR int next_arg_id() { - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; - } - - /** - Reports an error if using the automatic argument indexing; otherwise - switches to the manual indexing. - */ - FMT_CONSTEXPR void check_arg_id(int) { - if (next_arg_id_ > 0) - on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; - } - - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - - FMT_CONSTEXPR void on_error(const char* message) { - ErrorHandler::on_error(message); - } - - FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } -}; - -using format_parse_context = basic_format_parse_context; -using wformat_parse_context = basic_format_parse_context; - -template -using basic_parse_context FMT_DEPRECATED_ALIAS = - basic_format_parse_context; -using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context; -using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context; - -template class basic_format_arg; -template class basic_format_args; - -// A formatter for objects of type T. -template -struct formatter { - // A deleted default constructor indicates a disabled formatter. - formatter() = delete; -}; - -template -struct FMT_DEPRECATED convert_to_int - : bool_constant::value && - std::is_convertible::value> {}; - -// Specifies if T has an enabled formatter specialization. A type can be -// formattable even if it doesn't have a formatter e.g. via a conversion. -template -using has_formatter = - std::is_constructible>; - -namespace internal { - -/** A contiguous memory buffer with an optional growing ability. */ -template class buffer { - private: - T* ptr_; - std::size_t size_; - std::size_t capacity_; - - protected: - // Don't initialize ptr_ since it is not accessed to save a few cycles. - buffer(std::size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} - - buffer(T* p = nullptr, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT - : ptr_(p), - size_(sz), - capacity_(cap) {} - - /** Sets the buffer data and capacity. */ - void set(T* buf_data, std::size_t buf_capacity) FMT_NOEXCEPT { - ptr_ = buf_data; - capacity_ = buf_capacity; - } - - /** Increases the buffer capacity to hold at least *capacity* elements. */ - virtual void grow(std::size_t capacity) = 0; - - public: - using value_type = T; - using const_reference = const T&; - - buffer(const buffer&) = delete; - void operator=(const buffer&) = delete; - virtual ~buffer() = default; - - T* begin() FMT_NOEXCEPT { return ptr_; } - T* end() FMT_NOEXCEPT { return ptr_ + size_; } - - /** Returns the size of this buffer. */ - std::size_t size() const FMT_NOEXCEPT { return size_; } - - /** Returns the capacity of this buffer. */ - std::size_t capacity() const FMT_NOEXCEPT { return capacity_; } - - /** Returns a pointer to the buffer data. */ - T* data() FMT_NOEXCEPT { return ptr_; } - - /** Returns a pointer to the buffer data. */ - const T* data() const FMT_NOEXCEPT { return ptr_; } - - /** - Resizes the buffer. If T is a POD type new elements may not be initialized. - */ - void resize(std::size_t new_size) { - reserve(new_size); - size_ = new_size; - } - - /** Clears this buffer. */ - void clear() { size_ = 0; } - - /** Reserves space to store at least *capacity* elements. */ - void reserve(std::size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); - } - - void push_back(const T& value) { - reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); - - T& operator[](std::size_t index) { return ptr_[index]; } - const T& operator[](std::size_t index) const { return ptr_[index]; } -}; - -// A container-backed buffer. -template -class container_buffer : public buffer { - private: - Container& container_; - - protected: - void grow(std::size_t capacity) FMT_OVERRIDE { - container_.resize(capacity); - this->set(&container_[0], capacity); - } - - public: - explicit container_buffer(Container& c) - : buffer(c.size()), container_(c) {} -}; - -// Extracts a reference to the container from back_insert_iterator. -template -inline Container& get_container(std::back_insert_iterator it) { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; - }; - return *accessor(it).container; -} - -template -struct fallback_formatter { - fallback_formatter() = delete; -}; - -// Specifies if T has an enabled fallback_formatter specialization. -template -using has_fallback_formatter = - std::is_constructible>; - -template struct named_arg_base; -template struct named_arg; - -enum type { - none_type, - named_arg_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant : std::integral_constant {} - -FMT_TYPE_CONSTANT(const named_arg_base&, named_arg_type); -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_t, int128_type); -FMT_TYPE_CONSTANT(uint128_t, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -FMT_CONSTEXPR bool is_integral_type(type t) { - FMT_ASSERT(t != named_arg_type, "invalid argument type"); - return t > none_type && t <= last_integer_type; -} - -FMT_CONSTEXPR bool is_arithmetic_type(type t) { - FMT_ASSERT(t != named_arg_type, "invalid argument type"); - return t > none_type && t <= last_numeric_type; -} - -template struct string_value { - const Char* data; - std::size_t size; -}; - -template struct custom_value { - using parse_context = basic_format_parse_context; - const void* value; - void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); -}; - -// A formatting argument value. -template class value { - public: - using char_type = typename Context::char_type; - - union { - int int_value; - unsigned uint_value; - long long long_long_value; - unsigned long long ulong_long_value; - int128_t int128_value; - uint128_t uint128_value; - bool bool_value; - char_type char_value; - float float_value; - double double_value; - long double long_double_value; - const void* pointer; - string_value string; - custom_value custom; - const named_arg_base* named_arg; - }; - - FMT_CONSTEXPR value(int val = 0) : int_value(val) {} - FMT_CONSTEXPR value(unsigned val) : uint_value(val) {} - value(long long val) : long_long_value(val) {} - value(unsigned long long val) : ulong_long_value(val) {} - value(int128_t val) : int128_value(val) {} - value(uint128_t val) : uint128_value(val) {} - value(float val) : float_value(val) {} - value(double val) : double_value(val) {} - value(long double val) : long_double_value(val) {} - value(bool val) : bool_value(val) {} - value(char_type val) : char_value(val) {} - value(const char_type* val) { string.data = val; } - value(basic_string_view val) { - string.data = val.data(); - string.size = val.size(); - } - value(const void* val) : pointer(val) {} - - template value(const T& val) { - custom.value = &val; - // Get the formatter type through the context to allow different contexts - // have different extension points, e.g. `formatter` for `format` and - // `printf_formatter` for `printf`. - custom.format = format_custom_arg< - T, conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>>; - } - - value(const named_arg_base& val) { named_arg = &val; } - - private: - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - const void* arg, basic_format_parse_context& parse_ctx, - Context& ctx) { - Formatter f; - parse_ctx.advance_to(f.parse(parse_ctx)); - ctx.advance_to(f.format(*static_cast(arg), ctx)); - } -}; - -template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value); - -// To minimize the number of types we need to deal with, long is translated -// either to int or to long long depending on its size. -enum { long_short = sizeof(long) == sizeof(int) }; -using long_type = conditional_t; -using ulong_type = conditional_t; - -// Maps formatting arguments to core types. -template struct arg_mapper { - using char_type = typename Context::char_type; - - FMT_CONSTEXPR int map(signed char val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } - FMT_CONSTEXPR int map(short val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } - FMT_CONSTEXPR int map(int val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned val) { return val; } - FMT_CONSTEXPR long_type map(long val) { return val; } - FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } - FMT_CONSTEXPR long long map(long long val) { return val; } - FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } - FMT_CONSTEXPR int128_t map(int128_t val) { return val; } - FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } - FMT_CONSTEXPR bool map(bool val) { return val; } - - template ::value)> - FMT_CONSTEXPR char_type map(T val) { - static_assert( - std::is_same::value || std::is_same::value, - "mixing character types is disallowed"); - return val; - } - - FMT_CONSTEXPR float map(float val) { return val; } - FMT_CONSTEXPR double map(double val) { return val; } - FMT_CONSTEXPR long double map(long double val) { return val; } - - FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } - FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } - template ::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - static_assert(std::is_same>::value, - "mixing character types is disallowed"); - return to_string_view(val); - } - template , T>::value && - !is_string::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - return basic_string_view(val); - } - template < - typename T, - FMT_ENABLE_IF( - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - return std_string_view(val); - } - FMT_CONSTEXPR const char* map(const signed char* val) { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); - } - FMT_CONSTEXPR const char* map(const unsigned char* val) { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); - } - - FMT_CONSTEXPR const void* map(void* val) { return val; } - FMT_CONSTEXPR const void* map(const void* val) { return val; } - FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } - template FMT_CONSTEXPR int map(const T*) { - // Formatting of arbitrary pointers is disallowed. If you want to output - // a pointer cast it to "void *" or "const void *". In particular, this - // forbids formatting of "[const] volatile char *" which is printed as bool - // by iostreams. - static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); - return 0; - } - - template ::value && - !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR auto map(const T& val) -> decltype( - map(static_cast::type>(val))) { - return map(static_cast::type>(val)); - } - template < - typename T, - FMT_ENABLE_IF( - !is_string::value && !is_char::value && - !std::is_constructible, T>::value && - (has_formatter::value || - (has_fallback_formatter::value && - !std::is_constructible, T>::value)))> - FMT_CONSTEXPR const T& map(const T& val) { - return val; - } - - template - FMT_CONSTEXPR const named_arg_base& map( - const named_arg& val) { - auto arg = make_arg(val.value); - std::memcpy(val.data, &arg, sizeof(arg)); - return val; - } -}; - -// A type constant after applying arg_mapper. -template -using mapped_type_constant = - type_constant().map(std::declval())), - typename Context::char_type>; - -enum { packed_arg_bits = 5 }; -// Maximum number of arguments with packed types. -enum { max_packed_args = 63 / packed_arg_bits }; -enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; - -template class arg_map; -} // namespace internal - -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in basic_memory_buffer. -template class basic_format_arg { - private: - internal::value value_; - internal::type type_; - - template - friend FMT_CONSTEXPR basic_format_arg internal::make_arg( - const T& value); - - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)); - - friend class basic_format_args; - friend class internal::arg_map; - - using char_type = typename Context::char_type; - - public: - class handle { - public: - explicit handle(internal::custom_value custom) : custom_(custom) {} - - void format(basic_format_parse_context& parse_ctx, - Context& ctx) const { - custom_.format(custom_.value, parse_ctx, ctx); - } - - private: - internal::custom_value custom_; - }; - - FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {} - - FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT { - return type_ != internal::none_type; - } - - internal::type type() const { return type_; } - - bool is_integral() const { return internal::is_integral_type(type_); } - bool is_arithmetic() const { return internal::is_arithmetic_type(type_); } -}; - -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -template -FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)) { - using char_type = typename Context::char_type; - switch (arg.type_) { - case internal::none_type: - break; - case internal::named_arg_type: - FMT_ASSERT(false, "invalid argument type"); - break; - case internal::int_type: - return vis(arg.value_.int_value); - case internal::uint_type: - return vis(arg.value_.uint_value); - case internal::long_long_type: - return vis(arg.value_.long_long_value); - case internal::ulong_long_type: - return vis(arg.value_.ulong_long_value); -#if FMT_USE_INT128 - case internal::int128_type: - return vis(arg.value_.int128_value); - case internal::uint128_type: - return vis(arg.value_.uint128_value); -#else - case internal::int128_type: - case internal::uint128_type: - break; -#endif - case internal::bool_type: - return vis(arg.value_.bool_value); - case internal::char_type: - return vis(arg.value_.char_value); - case internal::float_type: - return vis(arg.value_.float_value); - case internal::double_type: - return vis(arg.value_.double_value); - case internal::long_double_type: - return vis(arg.value_.long_double_value); - case internal::cstring_type: - return vis(arg.value_.string.data); - case internal::string_type: - return vis(basic_string_view(arg.value_.string.data, - arg.value_.string.size)); - case internal::pointer_type: - return vis(arg.value_.pointer); - case internal::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); -} - -namespace internal { -// A map from argument names to their values for named arguments. -template class arg_map { - private: - using char_type = typename Context::char_type; - - struct entry { - basic_string_view name; - basic_format_arg arg; - }; - - entry* map_; - unsigned size_; - - void push_back(value val) { - const auto& named = *val.named_arg; - map_[size_] = {named.name, named.template deserialize()}; - ++size_; - } - - public: - arg_map(const arg_map&) = delete; - void operator=(const arg_map&) = delete; - arg_map() : map_(nullptr), size_(0) {} - void init(const basic_format_args& args); - ~arg_map() { delete[] map_; } - - basic_format_arg find(basic_string_view name) const { - // The list is unsorted, so just return the first matching name. - for (entry *it = map_, *end = map_ + size_; it != end; ++it) { - if (it->name == name) return it->arg; - } - return {}; - } -}; - -// A type-erased reference to an std::locale to avoid heavy include. -class locale_ref { - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - locale_ref() : locale_(nullptr) {} - template explicit locale_ref(const Locale& loc); - - explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } - - template Locale get() const; -}; - -template constexpr unsigned long long encode_types() { return 0; } - -template -constexpr unsigned long long encode_types() { - return mapped_type_constant::value | - (encode_types() << packed_arg_bits); -} - -template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -template -inline value make_arg(const T& val) { - return arg_mapper().map(val); -} - -template -inline basic_format_arg make_arg(const T& value) { - return make_arg(value); -} -} // namespace internal - -// Formatting context. -template class basic_format_context { - public: - /** The character type for the output. */ - using char_type = Char; - - private: - OutputIt out_; - basic_format_args args_; - internal::arg_map map_; - internal::locale_ref loc_; - - public: - using iterator = OutputIt; - using format_arg = basic_format_arg; - template using formatter_type = formatter; - - basic_format_context(const basic_format_context&) = delete; - void operator=(const basic_format_context&) = delete; - /** - Constructs a ``basic_format_context`` object. References to the arguments are - stored in the object so make sure they have appropriate lifetimes. - */ - basic_format_context(OutputIt out, - basic_format_args ctx_args, - internal::locale_ref loc = internal::locale_ref()) - : out_(out), args_(ctx_args), loc_(loc) {} - - format_arg arg(int id) const { return args_.get(id); } - - // Checks if manual indexing is used and returns the argument with the - // specified name. - format_arg arg(basic_string_view name); - - internal::error_handler error_handler() { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } - - // Returns an iterator to the beginning of the output range. - iterator out() { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { out_ = it; } - - internal::locale_ref locale() { return loc_; } -}; - -template -using buffer_context = - basic_format_context>, - Char>; -using format_context = buffer_context; -using wformat_context = buffer_context; - -/** - \rst - An array of references to arguments. It can be implicitly converted into - `~fmt::basic_format_args` for passing into type-erased formatting functions - such as `~fmt::vformat`. - \endrst - */ -template class format_arg_store { - private: - static const size_t num_args = sizeof...(Args); - static const bool is_packed = num_args < internal::max_packed_args; - - using value_type = conditional_t, - basic_format_arg>; - - // If the arguments are not packed, add one more element to mark the end. - value_type data_[num_args + (num_args == 0 ? 1 : 0)]; - - friend class basic_format_args; - - public: - static constexpr unsigned long long types = - is_packed ? internal::encode_types() - : internal::is_unpacked_bit | num_args; - - format_arg_store(const Args&... args) - : data_{internal::make_arg(args)...} {} -}; - -/** - \rst - Constructs an `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ -template -inline format_arg_store make_format_args( - const Args&... args) { - return {args...}; -} - -/** Formatting arguments. */ -template class basic_format_args { - public: - using size_type = int; - using format_arg = basic_format_arg; - - private: - // To reduce compiled code size per formatting function call, types of first - // max_packed_args arguments are passed in the types_ field. - unsigned long long types_; - union { - // If the number of arguments is less than max_packed_args, the argument - // values are stored in values_, otherwise they are stored in args_. - // This is done to reduce compiled code size as storing larger objects - // may require more code (at least on x86-64) even if the same amount of - // data is actually copied to stack. It saves ~10% on the bloat test. - const internal::value* values_; - const format_arg* args_; - }; - - bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; } - - internal::type type(int index) const { - int shift = index * internal::packed_arg_bits; - unsigned int mask = (1 << internal::packed_arg_bits) - 1; - return static_cast((types_ >> shift) & mask); - } - - friend class internal::arg_map; - - void set_data(const internal::value* values) { values_ = values; } - void set_data(const format_arg* args) { args_ = args; } - - format_arg do_get(int index) const { - format_arg arg; - if (!is_packed()) { - auto num_args = max_size(); - if (index < num_args) arg = args_[index]; - return arg; - } - if (index > internal::max_packed_args) return arg; - arg.type_ = type(index); - if (arg.type_ == internal::none_type) return arg; - internal::value& val = arg.value_; - val = values_[index]; - return arg; - } - - public: - basic_format_args() : types_(0) {} - - /** - \rst - Constructs a `basic_format_args` object from `~fmt::format_arg_store`. - \endrst - */ - template - basic_format_args(const format_arg_store& store) - : types_(store.types) { - set_data(store.data_); - } - - /** - \rst - Constructs a `basic_format_args` object from a dynamic set of arguments. - \endrst - */ - basic_format_args(const format_arg* args, int count) - : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) { - set_data(args); - } - - /** Returns the argument at specified index. */ - format_arg get(int index) const { - format_arg arg = do_get(index); - if (arg.type_ == internal::named_arg_type) - arg = arg.value_.named_arg->template deserialize(); - return arg; - } - - int max_size() const { - unsigned long long max_packed = internal::max_packed_args; - return static_cast(is_packed() ? max_packed - : types_ & ~internal::is_unpacked_bit); - } -}; - -/** An alias to ``basic_format_args``. */ -// It is a separate type rather than an alias to make symbols readable. -struct format_args : basic_format_args { - template - format_args(Args&&... args) - : basic_format_args(std::forward(args)...) {} -}; -struct wformat_args : basic_format_args { - template - wformat_args(Args&&... args) - : basic_format_args(std::forward(args)...) {} -}; - -template struct is_contiguous : std::false_type {}; - -template -struct is_contiguous> : std::true_type {}; - -template -struct is_contiguous> : std::true_type {}; - -namespace internal { - -template -struct is_contiguous_back_insert_iterator : std::false_type {}; -template -struct is_contiguous_back_insert_iterator> - : is_contiguous {}; - -template struct named_arg_base { - basic_string_view name; - - // Serialized value. - mutable char data[sizeof(basic_format_arg>)]; - - named_arg_base(basic_string_view nm) : name(nm) {} - - template basic_format_arg deserialize() const { - basic_format_arg arg; - std::memcpy(&arg, data, sizeof(basic_format_arg)); - return arg; - } -}; - -template struct named_arg : named_arg_base { - const T& value; - - named_arg(basic_string_view name, const T& val) - : named_arg_base(name), value(val) {} -}; - -template ::value)> -inline void check_format_string(const S&) { -#if defined(FMT_ENFORCE_COMPILE_STRING) - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to " - "utilize FMT_STRING() or fmt()."); -#endif -} -template ::value)> -void check_format_string(S); - -struct view {}; -template struct bool_pack; -template -using all_true = - std::is_same, bool_pack>; - -template > -inline format_arg_store, remove_reference_t...> -make_args_checked(const S& format_str, - const remove_reference_t&... args) { - static_assert(all_true<(!std::is_base_of>() || - !std::is_reference())...>::value, - "passing views as lvalues is disallowed"); - check_format_string>...>(format_str); - return {args...}; -} - -template -std::basic_string vformat(basic_string_view format_str, - basic_format_args> args); - -template -typename buffer_context::iterator vformat_to( - buffer& buf, basic_string_view format_str, - basic_format_args> args); -} // namespace internal - -/** - \rst - Returns a named argument to be used in a formatting function. - - The named argument holds a reference and does not extend the lifetime - of its arguments. - Consequently, a dangling reference can accidentally be created. - The user should take care to only pass this function temporaries when - the named argument is itself a temporary, as per the following example. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst - */ -template > -inline internal::named_arg arg(const S& name, const T& arg) { - static_assert(internal::is_string::value, ""); - return {name, arg}; -} - -// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``. -template -void arg(S, internal::named_arg) = delete; - -/** Formats a string and writes the output to ``out``. */ -// GCC 8 and earlier cannot handle std::back_insert_iterator with -// vformat_to(...) overload, so SFINAE on iterator type instead. -template , - FMT_ENABLE_IF( - internal::is_contiguous_back_insert_iterator::value)> -OutputIt vformat_to(OutputIt out, const S& format_str, - basic_format_args> args) { - using container = remove_reference_t; - internal::container_buffer buf((internal::get_container(out))); - internal::vformat_to(buf, to_string_view(format_str), args); - return out; -} - -template ::value&& internal::is_string::value)> -inline std::back_insert_iterator format_to( - std::back_insert_iterator out, const S& format_str, - Args&&... args) { - return vformat_to( - out, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); -} - -template > -inline std::basic_string vformat( - const S& format_str, basic_format_args> args) { - return internal::vformat(to_string_view(format_str), args); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}", 42); - \endrst -*/ -// Pass char_t as a default template parameter instead of using -// std::basic_string> to reduce the symbol size. -template > -inline std::basic_string format(const S& format_str, Args&&... args) { - return internal::vformat( - to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); -} - -FMT_API void vprint(std::FILE* f, string_view format_str, format_args args); -FMT_API void vprint(string_view format_str, format_args args); - -/** - \rst - Prints formatted data to the file *f*. For wide format strings, - *f* should be in wide-oriented mode set via ``fwide(f, 1)`` or - ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ -template ::value)> -inline void print(std::FILE* f, const S& format_str, Args&&... args) { - vprint(f, to_string_view(format_str), - internal::make_args_checked(format_str, args...)); -} - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -template ::value)> -inline void print(const S& format_str, Args&&... args) { - vprint(to_string_view(format_str), - internal::make_args_checked(format_str, args...)); -} -FMT_END_NAMESPACE - -#endif // FMT_CORE_H_ diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/format-inl.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/format-inl.h deleted file mode 100644 index 72b3046..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/format-inl.h +++ /dev/null @@ -1,1396 +0,0 @@ -// Formatting library for C++ - implementation -// -// Copyright (c) 2012 - 2016, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_FORMAT_INL_H_ -#define FMT_FORMAT_INL_H_ - -#include "format.h" - -#include -#include -#include -#include -#include -#include // for std::memmove -#include -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) -# include -#endif - -#if FMT_USE_WINDOWS_H -# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN -# endif -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# undef NOMINMAX -# endif -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4702) // unreachable code -#endif - -// Dummy implementations of strerror_r and strerror_s called if corresponding -// system functions are not available. -inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; } -inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; } - -FMT_BEGIN_NAMESPACE -namespace internal { - -FMT_FUNC void assert_fail(const char* file, int line, const char* message) { - print(stderr, "{}:{}: assertion failed: {}", file, line, message); - std::abort(); -} - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -using format_func = void (*)(internal::buffer&, int, string_view); - -// A portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -FMT_FUNC int safe_strerror(int error_code, char*& buffer, - std::size_t buffer_size) FMT_NOEXCEPT { - FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); - - class dispatcher { - private: - int error_code_; - char*& buffer_; - std::size_t buffer_size_; - - // A noop assignment operator to avoid bogus warnings. - void operator=(const dispatcher&) {} - - // Handle the result of XSI-compliant version of strerror_r. - int handle(int result) { - // glibc versions before 2.13 return result in errno. - return result == -1 ? errno : result; - } - - // Handle the result of GNU-specific version of strerror_r. - int handle(char* message) { - // If the buffer is full then the message is probably truncated. - if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) - return ERANGE; - buffer_ = message; - return 0; - } - - // Handle the case when strerror_r is not available. - int handle(internal::null<>) { - return fallback(strerror_s(buffer_, buffer_size_, error_code_)); - } - - // Fallback to strerror_s when strerror_r is not available. - int fallback(int result) { - // If the buffer is full then the message is probably truncated. - return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE - : result; - } - -#if !FMT_MSC_VER - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(internal::null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } -#endif - - public: - dispatcher(int err_code, char*& buf, std::size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } - }; - return dispatcher(error_code, buffer, buffer_size).run(); -} - -FMT_FUNC void format_error_code(internal::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // inline_buffer_size to avoid dynamic memory allocation and potential - // bad_alloc. - out.resize(0); - static const char SEP[] = ": "; - static const char ERROR_STR[] = "error "; - // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. - std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; - auto abs_value = static_cast>(error_code); - if (internal::is_negative(error_code)) { - abs_value = 0 - abs_value; - ++error_code_size; - } - error_code_size += internal::to_unsigned(internal::count_digits(abs_value)); - internal::writer w(out); - if (message.size() <= inline_buffer_size - error_code_size) { - w.write(message); - w.write(SEP); - } - w.write(ERROR_STR); - w.write(error_code); - assert(out.size() <= inline_buffer_size); -} - -// A wrapper around fwrite that throws on error. -FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count, - FILE* stream) { - size_t written = std::fwrite(ptr, size, count, stream); - if (written < count) { - FMT_THROW(system_error(errno, "cannot write to file")); - } -} - -FMT_FUNC void report_error(format_func func, int error_code, - string_view message) FMT_NOEXCEPT { - memory_buffer full_message; - func(full_message, error_code, message); - // Don't use fwrite_fully because the latter may throw. - (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} -} // namespace internal - -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) -namespace internal { - -template -locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same::value, ""); -} - -template Locale locale_ref::get() const { - static_assert(std::is_same::value, ""); - return locale_ ? *static_cast(locale_) : std::locale(); -} - -template FMT_FUNC std::string grouping_impl(locale_ref loc) { - return std::use_facet>(loc.get()).grouping(); -} -template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .thousands_sep(); -} -template FMT_FUNC Char decimal_point_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .decimal_point(); -} -} // namespace internal -#else -template -FMT_FUNC std::string internal::grouping_impl(locale_ref) { - return "\03"; -} -template -FMT_FUNC Char internal::thousands_sep_impl(locale_ref) { - return FMT_STATIC_THOUSANDS_SEPARATOR; -} -template -FMT_FUNC Char internal::decimal_point_impl(locale_ref) { - return '.'; -} -#endif - -FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; -FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; - -FMT_FUNC void system_error::init(int err_code, string_view format_str, - format_args args) { - error_code_ = err_code; - memory_buffer buffer; - format_system_error(buffer, err_code, vformat(format_str, args)); - std::runtime_error& base = *this; - base = std::runtime_error(to_string(buffer)); -} - -namespace internal { - -template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) { - // fallback_uintptr is always stored in little endian. - int i = static_cast(sizeof(void*)) - 1; - while (i > 0 && n.value[i] == 0) --i; - auto char_digits = std::numeric_limits::digits / 4; - return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; -} - -template -const char basic_data::digits[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -template -const char basic_data::hex_digits[] = "0123456789abcdef"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 - -template -const uint64_t basic_data::powers_of_10_64[] = { - 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - -template -const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, - FMT_POWERS_OF_10(1)}; - -template -const uint64_t basic_data::zero_or_powers_of_10_64[] = { - 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - -// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. -// These are generated by support/compute-powers.py. -template -const uint64_t basic_data::pow10_significands[] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, - 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, -}; - -// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding -// to significands above. -template -const int16_t basic_data::pow10_exponents[] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, - -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, - -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, - -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, - -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, - 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, - 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, - 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; - -template -const char basic_data::foreground_color[] = "\x1b[38;2;"; -template -const char basic_data::background_color[] = "\x1b[48;2;"; -template const char basic_data::reset_color[] = "\x1b[0m"; -template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; -template const char basic_data::signs[] = {0, '-', '+', ' '}; - -template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); -}; - -class fp; -template fp normalize(fp value); - -// Lower (upper) boundary is a value half way between a floating-point value -// and its predecessor (successor). Boundaries have the same exponent as the -// value so only significands are stored. -struct boundaries { - uint64_t lower; - uint64_t upper; -}; - -// A handmade floating-point number f * pow(2, e). -class fp { - private: - using significand_type = uint64_t; - - // All sizes are in bits. - // Subtract 1 to account for an implicit most significant bit in the - // normalized form. - static FMT_CONSTEXPR_DECL const int double_significand_size = - std::numeric_limits::digits - 1; - static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = - 1ULL << double_significand_size; - - public: - significand_type f; - int e; - - static FMT_CONSTEXPR_DECL const int significand_size = - bits::value; - - fp() : f(0), e(0) {} - fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - - // Constructs fp from an IEEE754 double. It is a template to prevent compile - // errors on platforms where double is not IEEE754. - template explicit fp(Double d) { assign(d); } - - // Normalizes the value converted from double and multiplied by (1 << SHIFT). - template friend fp normalize(fp value) { - // Handle subnormals. - const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; - while ((value.f & shifted_implicit_bit) == 0) { - value.f <<= 1; - --value.e; - } - // Subtract 1 to account for hidden bit. - const auto offset = - fp::significand_size - fp::double_significand_size - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; - } - - // Assigns d to this and return true iff predecessor is closer than successor. - template - bool assign(Double d) { - // Assume double is in the format [sign][exponent][significand]. - using limits = std::numeric_limits; - const int exponent_size = - bits::value - double_significand_size - 1; // -1 for sign - const uint64_t significand_mask = implicit_bit - 1; - const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; - const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; - auto u = bit_cast(d); - f = u & significand_mask; - auto biased_e = (u & exponent_mask) >> double_significand_size; - // Predecessor is closer if d is a normalized power of 2 (f == 0) other than - // the smallest normalized number (biased_e > 1). - bool is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e != 0) - f += implicit_bit; - else - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = static_cast(biased_e - exponent_bias - double_significand_size); - return is_predecessor_closer; - } - - template - bool assign(Double) { - *this = fp(); - return false; - } - - // Assigns d to this together with computing lower and upper boundaries, - // where a boundary is a value half way between the number and its predecessor - // (lower) or successor (upper). The upper boundary is normalized and lower - // has the same exponent but may be not normalized. - template boundaries assign_with_boundaries(Double d) { - bool is_lower_closer = assign(d); - fp lower = - is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1); - // 1 in normalize accounts for the exponent shift above. - fp upper = normalize<1>(fp((f << 1) + 1, e - 1)); - lower.f <<= lower.e - upper.e; - return boundaries{lower.f, upper.f}; - } - - template boundaries assign_float_with_boundaries(Double d) { - assign(d); - constexpr int min_normal_e = std::numeric_limits::min_exponent - - std::numeric_limits::digits; - significand_type half_ulp = 1 << (std::numeric_limits::digits - - std::numeric_limits::digits - 1); - if (min_normal_e > e) half_ulp <<= min_normal_e - e; - fp upper = normalize<0>(fp(f + half_ulp, e)); - fp lower = fp( - f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e); - lower.f <<= lower.e - upper.e; - return boundaries{lower.f, upper.f}; - } -}; - -inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } - -// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -#if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); - return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; -#else - // Multiply 32-bit parts of significands. - uint64_t mask = (1ULL << 32) - 1; - uint64_t a = lhs >> 32, b = lhs & mask; - uint64_t c = rhs >> 32, d = rhs & mask; - uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; - // Compute mid 64-bit of result and round. - uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); - return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); -#endif -} - -inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } - -// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its -// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) { - const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) - int index = static_cast( - static_cast( - (min_exponent + fp::significand_size - 1) * one_over_log2_10 + - ((uint64_t(1) << 32) - 1) // ceil - ) >> - 32 // arithmetic shift - ); - // Decimal exponent of the first (smallest) cached power of 10. - const int first_dec_exp = -348; - // Difference between 2 consecutive decimal exponents in cached powers of 10. - const int dec_exp_step = 8; - index = (index - first_dec_exp - 1) / dec_exp_step + 1; - pow10_exponent = first_dec_exp + index * dec_exp_step; - return {data::pow10_significands[index], data::pow10_exponents[index]}; -} - -// A simple accumulator to hold the sums of terms in bigint::square if uint128_t -// is not available. -struct accumulator { - uint64_t lower; - uint64_t upper; - - accumulator() : lower(0), upper(0) {} - explicit operator uint32_t() const { return static_cast(lower); } - - void operator+=(uint64_t n) { - lower += n; - if (lower < n) ++upper; - } - void operator>>=(int shift) { - assert(shift == 32); - (void)shift; - lower = (upper << 32) | (lower >> 32); - upper >>= 32; - } -}; - -class bigint { - private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; - using double_bigit = uint64_t; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - - void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast(bigits_[index]) - other - borrow; - bigits_[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - - void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; - bigits_.resize(num_bigits + 1); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. - void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; - for (int j = 0, n = static_cast(other.bigits_.size()); j != n; - ++i, ++j) { - subtract_bigits(i, other.bigits_[j], borrow); - } - while (borrow > 0) subtract_bigits(i, 0, borrow); - remove_leading_zeros(); - } - - void multiply(uint32_t value) { - const double_bigit wide_value = value; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * wide_value + carry; - bigits_[i] = static_cast(result); - carry = static_cast(result >> bigit_bits); - } - if (carry != 0) bigits_.push_back(carry); - } - - void multiply(uint64_t value) { - const bigit mask = ~bigit(0); - const double_bigit lower = value & mask; - const double_bigit upper = value >> bigit_bits; - double_bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * lower + (carry & mask); - carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); - bigits_[i] = static_cast(result); - } - while (carry != 0) { - bigits_.push_back(carry & mask); - carry >>= bigit_bits; - } - } - - public: - bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - - void assign(const bigint& other) { - bigits_.resize(other.bigits_.size()); - auto data = other.bigits_.data(); - std::copy(data, data + other.bigits_.size(), bigits_.data()); - exp_ = other.exp_; - } - - void assign(uint64_t n) { - int num_bigits = 0; - do { - bigits_[num_bigits++] = n & ~bigit(0); - n >>= bigit_bits; - } while (n != 0); - bigits_.resize(num_bigits); - exp_ = 0; - } - - int num_bigits() const { return static_cast(bigits_.size()) + exp_; } - - bigint& operator<<=(int shift) { - assert(shift >= 0); - exp_ += shift / bigit_bits; - shift %= bigit_bits; - if (shift == 0) return *this; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - bigit c = bigits_[i] >> (bigit_bits - shift); - bigits_[i] = (bigits_[i] << shift) + carry; - carry = c; - } - if (carry != 0) bigits_.push_back(carry); - return *this; - } - - template bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - - friend int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). - friend int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0; - }; - double_bigit borrow = 0; - int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; - borrow <<= bigit_bits; - } - return borrow != 0 ? -1 : 0; - } - - // Assigns pow(10, exp) to this bigint. - void assign_pow10(int exp) { - assert(exp >= 0); - if (exp == 0) return assign(1); - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - assign(5); - bitmask >>= 1; - while (bitmask != 0) { - square(); - if ((exp & bitmask) != 0) *this *= 5; - bitmask >>= 1; - } - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - - void square() { - basic_memory_buffer n(std::move(bigits_)); - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; - bigits_.resize(num_result_bigits); - using accumulator_t = conditional_t; - auto sum = accumulator_t(); - for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { - // Compute bigit at position bigit_index of the result by adding - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; - } - bigits_[bigit_index] = static_cast(sum); - sum >>= bits::value; // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - bigits_[bigit_index] = static_cast(sum); - sum >>= bits::value; - } - --num_result_bigits; - remove_leading_zeros(); - exp_ *= 2; - } - - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. - int divmod_assign(const bigint& divisor) { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; - int num_bigits = static_cast(bigits_.size()); - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, ""); - int exp_difference = exp_ - divisor.exp_; - if (exp_difference > 0) { - // Align bigints by adding trailing zeros to simplify subtraction. - bigits_.resize(num_bigits + exp_difference); - for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) - bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); - exp_ -= exp_difference; - } - int quotient = 0; - do { - subtract_aligned(divisor); - ++quotient; - } while (compare(*this, divisor) >= 0); - return quotient; - } -}; - -enum round_direction { unknown, up, down }; - -// Given the divisor (normally a power of 10), the remainder = v % divisor for -// some number v and the error, returns whether v should be rounded up, down, or -// whether the rounding direction can't be determined due to error. -// error should be less than divisor / 2. -inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, - uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. - FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. - // Round down if (remainder + error) * 2 <= divisor. - if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) - return down; - // Round up if (remainder - error) * 2 >= divisor. - if (remainder >= error && - remainder - error >= divisor - (remainder - error)) { - return up; - } - return unknown; -} - -namespace digits { -enum result { - more, // Generate more digits. - done, // Done generating digits. - error // Digit generation cancelled due to an error. -}; -} - -// Generates output using the Grisu digit-gen algorithm. -// error: the size of the region (lower, upper) outside of which numbers -// definitely do not round to value (Delta in Grisu3). -template -FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - int& exp, Handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due - // to normalization) - 1, shifted right by at most 60 bits. - auto integral = static_cast(value.f >> -one.e); - FMT_ASSERT(integral != 0, ""); - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); - exp = count_digits(integral); // kappa in Grisu. - // Divide by 10 to prevent overflow. - auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, - value.f / 10, error * 10, exp); - if (result != digits::more) return result; - // Generate digits for the integral part. This can produce up to 10 digits. - do { - uint32_t digit = 0; - auto divmod_integral = [&](uint32_t divisor) { - digit = integral / divisor; - integral %= divisor; - }; - // This optimization by Milo Yip reduces the number of integer divisions by - // one per iteration. - switch (exp) { - case 10: - divmod_integral(1000000000); - break; - case 9: - divmod_integral(100000000); - break; - case 8: - divmod_integral(10000000); - break; - case 7: - divmod_integral(1000000); - break; - case 6: - divmod_integral(100000); - break; - case 5: - divmod_integral(10000); - break; - case 4: - divmod_integral(1000); - break; - case 3: - divmod_integral(100); - break; - case 2: - divmod_integral(10); - break; - case 1: - digit = integral; - integral = 0; - break; - default: - FMT_ASSERT(false, "invalid number of digits"); - } - --exp; - uint64_t remainder = - (static_cast(integral) << -one.e) + fractional; - result = handler.on_digit(static_cast('0' + digit), - data::powers_of_10_64[exp] << -one.e, remainder, - error, exp, true); - if (result != digits::more) return result; - } while (exp > 0); - // Generate digits for the fractional part. - for (;;) { - fractional *= 10; - error *= 10; - char digit = - static_cast('0' + static_cast(fractional >> -one.e)); - fractional &= one.f - 1; - --exp; - result = handler.on_digit(digit, one.f, fractional, error, exp, false); - if (result != digits::more) return result; - } -} - -// The fixed precision digit handler. -struct fixed_handler { - char* buf; - int size; - int precision; - int exp10; - bool fixed; - - digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, - int& exp) { - // Non-fixed formats require at least one digit and no precision adjustment. - if (!fixed) return digits::more; - // Adjust fixed precision by exponent because it is relative to decimal - // point. - precision += exp + exp10; - // Check if precision is satisfied just by leading zeros, e.g. - // format("{:.2f}", 0.001) gives "0.00" without generating any digits. - if (precision > 0) return digits::more; - if (precision < 0) return digits::done; - auto dir = get_round_direction(divisor, remainder, error); - if (dir == unknown) return digits::error; - buf[size++] = dir == up ? '1' : '0'; - return digits::done; - } - - digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, - uint64_t error, int, bool integral) { - FMT_ASSERT(remainder < divisor, ""); - buf[size++] = digit; - if (size < precision) return digits::more; - if (!integral) { - // Check if error * 2 < divisor with overflow prevention. - // The check is not needed for the integral part because error = 1 - // and divisor > (1 << 32) there. - if (error >= divisor || error >= divisor - error) return digits::error; - } else { - FMT_ASSERT(error == 1 && divisor > 2, ""); - } - auto dir = get_round_direction(divisor, remainder, error); - if (dir != up) return dir == down ? digits::done : digits::error; - ++buf[size - 1]; - for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] > '9') { - buf[0] = '1'; - buf[size++] = '0'; - } - return digits::done; - } -}; - -// The shortest representation digit handler. -struct grisu_shortest_handler { - char* buf; - int size; - // Distance between scaled value and upper bound (wp_W in Grisu3). - uint64_t diff; - - digits::result on_start(uint64_t, uint64_t, uint64_t, int&) { - return digits::more; - } - - // Decrement the generated number approaching value from above. - void round(uint64_t d, uint64_t divisor, uint64_t& remainder, - uint64_t error) { - while ( - remainder < d && error - remainder >= divisor && - (remainder + divisor < d || d - remainder >= remainder + divisor - d)) { - --buf[size - 1]; - remainder += divisor; - } - } - - // Implements Grisu's round_weed. - digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, - uint64_t error, int exp, bool integral) { - buf[size++] = digit; - if (remainder >= error) return digits::more; - uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp]; - uint64_t up = (diff - 1) * unit; // wp_Wup - round(up, divisor, remainder, error); - uint64_t down = (diff + 1) * unit; // wp_Wdown - if (remainder < down && error - remainder >= divisor && - (remainder + divisor < down || - down - remainder > remainder + divisor - down)) { - return digits::error; - } - return 2 * unit <= remainder && remainder <= error - 4 * unit - ? digits::done - : digits::error; - } -}; - -// Formats value using a variation of the Fixed-Precision Positive -// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: -// https://fmt.dev/p372-steele.pdf. -template -void fallback_format(Double d, buffer& buf, int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). - fp value; - // Shift numerator and denominator by an extra bit or two (if lower boundary - // is closer) to make lower and upper integers. This eliminates multiplication - // by 2 during later computations. - // TODO: handle float - int shift = value.assign(d) ? 2 : 1; - uint64_t significand = value.f << shift; - if (value.e >= 0) { - numerator.assign(significand); - numerator <<= value.e; - lower.assign(1); - lower <<= value.e; - if (shift != 1) { - upper_store.assign(1); - upper_store <<= value.e + 1; - upper = &upper_store; - } - denominator.assign_pow10(exp10); - denominator <<= 1; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); - if (shift != 1) { - upper_store.assign(numerator); - upper_store <<= 1; - upper = &upper_store; - } - numerator *= significand; - denominator.assign(1); - denominator <<= shift - value.e; - } else { - numerator.assign(significand); - denominator.assign_pow10(exp10); - denominator <<= shift - value.e; - lower.assign(1); - if (shift != 1) { - upper_store.assign(1ULL << 1); - upper = &upper_store; - } - } - if (!upper) upper = &lower; - // Invariant: value == (numerator / denominator) * pow(10, exp10). - bool even = (value.f & 1) == 0; - int num_digits = 0; - char* data = buf.data(); - for (;;) { - int digit = numerator.divmod_assign(denominator); - bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. - // numerator + upper >[=] pow10: - bool high = add_compare(numerator, *upper, denominator) + even > 0; - data[num_digits++] = static_cast('0' + digit); - if (low || high) { - if (!low) { - ++data[num_digits - 1]; - } else if (high) { - int result = add_compare(numerator, numerator, denominator); - // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; - } - buf.resize(num_digits); - exp10 -= num_digits - 1; - return; - } - numerator *= 10; - lower *= 10; - if (upper != &lower) *upper *= 10; - } -} - -// Formats value using the Grisu algorithm -// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) -// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. -template -int format_float(T value, int precision, float_specs specs, buffer& buf) { - static_assert(!std::is_same(), ""); - FMT_ASSERT(value >= 0, "value is negative"); - - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; - } - buf.resize(to_unsigned(precision)); - std::uninitialized_fill_n(buf.data(), precision, '0'); - return -precision; - } - - if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); - - int exp = 0; - const int min_exp = -60; // alpha in Grisu. - int cached_exp10 = 0; // K in Grisu. - if (precision != -1) { - if (precision > 17) return snprintf_float(value, precision, specs, buf); - fp normalized = normalize(fp(value)); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::significand_size), cached_exp10); - normalized = normalized * cached_pow; - fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; - if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) - return snprintf_float(value, precision, specs, buf); - int num_digits = handler.size; - if (!fixed) { - // Remove trailing zeros. - while (num_digits > 0 && buf[num_digits - 1] == '0') { - --num_digits; - ++exp; - } - } - buf.resize(to_unsigned(num_digits)); - } else { - fp fp_value; - auto boundaries = specs.binary32 - ? fp_value.assign_float_with_boundaries(value) - : fp_value.assign_with_boundaries(value); - fp_value = normalize(fp_value); - // Find a cached power of 10 such that multiplying value by it will bring - // the exponent in the range [min_exp, -32]. - const fp cached_pow = get_cached_power( - min_exp - (fp_value.e + fp::significand_size), cached_exp10); - // Multiply value and boundaries by the cached power of 10. - fp_value = fp_value * cached_pow; - boundaries.lower = multiply(boundaries.lower, cached_pow.f); - boundaries.upper = multiply(boundaries.upper, cached_pow.f); - assert(min_exp <= fp_value.e && fp_value.e <= -32); - --boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}. - ++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}. - // Numbers outside of (lower, upper) definitely do not round to value. - grisu_shortest_handler handler{buf.data(), 0, - boundaries.upper - fp_value.f}; - auto result = - grisu_gen_digits(fp(boundaries.upper, fp_value.e), - boundaries.upper - boundaries.lower, exp, handler); - if (result == digits::error) { - exp += handler.size - cached_exp10 - 1; - fallback_format(value, buf, exp); - return exp; - } - buf.resize(to_unsigned(handler.size)); - } - return exp - cached_exp10; -} - -template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf) { - // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same(), ""); - - // Subtract 1 to account for the difference in precision since we use %e for - // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) - precision = (precision >= 0 ? precision : 6) - 1; - - // Build the format string. - enum { max_format_size = 7 }; // Ths longest format is "%#.*Le". - char format[max_format_size]; - char* format_ptr = format; - *format_ptr++ = '%'; - if (specs.trailing_zeros) *format_ptr++ = '#'; - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); - *format_ptr = '\0'; - - // Format using snprintf. - auto offset = buf.size(); - for (;;) { - auto begin = buf.data() + offset; - auto capacity = buf.capacity() - offset; -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about a nonliteral format string. - auto snprintf_ptr = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { - buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially. - continue; - } - unsigned size = to_unsigned(result); - // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { - buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, fraction_size); - buf.resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { - buf.resize(size + offset); - return 0; - } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - assert(sign == '+' || sign == '-'); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - assert(is_digit(*p)); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, fraction_size); - } - buf.resize(fraction_size + offset + 1); - return exp - fraction_size; - } -} -} // namespace internal - -template <> struct formatter { - format_parse_context::iterator parse(format_parse_context& ctx) { - return ctx.begin(); - } - - format_context::iterator format(const internal::bigint& n, - format_context& ctx) { - auto out = ctx.out(); - bool first = true; - for (auto i = n.bigits_.size(); i > 0; --i) { - auto value = n.bigits_[i - 1]; - if (first) { - out = format_to(out, "{:x}", value); - first = false; - continue; - } - out = format_to(out, "{:08x}", value); - } - if (n.exp_ > 0) - out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits); - return out; - } -}; - -#if FMT_USE_WINDOWS_H - -FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) { - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (s.size() > INT_MAX) - FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG)); - int s_size = static_cast(s.size()); - if (s_size == 0) { - // MultiByteToWideChar does not support zero length, handle separately. - buffer_.resize(1); - buffer_[0] = 0; - return; - } - - int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), - s_size, nullptr, 0); - if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); - buffer_.resize(length + 1); - length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, - &buffer_[0], length); - if (length == 0) FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); - buffer_[length] = 0; -} - -FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { - if (int error_code = convert(s)) { - FMT_THROW(windows_error(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) { - if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; - int s_size = static_cast(s.size()); - if (s_size == 0) { - // WideCharToMultiByte does not support zero length, handle separately. - buffer_.resize(1); - buffer_[0] = 0; - return 0; - } - - int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, - nullptr, nullptr); - if (length == 0) return GetLastError(); - buffer_.resize(length + 1); - length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], - length, nullptr, nullptr); - if (length == 0) return GetLastError(); - buffer_[length] = 0; - return 0; -} - -FMT_FUNC void windows_error::init(int err_code, string_view format_str, - format_args args) { - error_code_ = err_code; - memory_buffer buffer; - internal::format_windows_error(buffer, err_code, vformat(format_str, args)); - std::runtime_error& base = *this; - base = std::runtime_error(to_string(buffer)); -} - -FMT_FUNC void internal::format_windows_error(internal::buffer& out, - int error_code, - string_view message) FMT_NOEXCEPT { - FMT_TRY { - wmemory_buffer buf; - buf.resize(inline_buffer_size); - for (;;) { - wchar_t* system_message = &buf[0]; - int result = FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, - static_cast(buf.size()), nullptr); - if (result != 0) { - utf16_to_utf8 utf8_message; - if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - internal::writer w(out); - w.write(message); - w.write(": "); - w.write(utf8_message); - return; - } - break; - } - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; // Can't get error message, report error code instead. - buf.resize(buf.size() * 2); - } - } - FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} - -#endif // FMT_USE_WINDOWS_H - -FMT_FUNC void format_system_error(internal::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { - FMT_TRY { - memory_buffer buf; - buf.resize(inline_buffer_size); - for (;;) { - char* system_message = &buf[0]; - int result = - internal::safe_strerror(error_code, system_message, buf.size()); - if (result == 0) { - internal::writer w(out); - w.write(message); - w.write(": "); - w.write(system_message); - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buf.resize(buf.size() * 2); - } - } - FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} - -FMT_FUNC void internal::error_handler::on_error(const char* message) { - FMT_THROW(format_error(message)); -} - -FMT_FUNC void report_system_error(int error_code, - fmt::string_view message) FMT_NOEXCEPT { - report_error(format_system_error, error_code, message); -} - -#if FMT_USE_WINDOWS_H -FMT_FUNC void report_windows_error(int error_code, - fmt::string_view message) FMT_NOEXCEPT { - report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { - memory_buffer buffer; - internal::vformat_to(buffer, format_str, - basic_format_args>(args)); - internal::fwrite_fully(buffer.data(), 1, buffer.size(), f); -} - -FMT_FUNC void vprint(string_view format_str, format_args args) { - vprint(stdout, format_str, args); -} - -FMT_END_NAMESPACE - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -#endif // FMT_FORMAT_INL_H_ diff --git a/thirdparty/spdlog/include/spdlog/fmt/bundled/format.h b/thirdparty/spdlog/include/spdlog/fmt/bundled/format.h deleted file mode 100644 index 01f41f5..0000000 --- a/thirdparty/spdlog/include/spdlog/fmt/bundled/format.h +++ /dev/null @@ -1,3541 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - present, Victor Zverovich - - 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. - - --- Optional exception to the license --- - - As an exception, if, as a result of your compiling your source code, portions - of this Software are embedded into a machine-executable object form of such - source code, you may redistribute such embedded portions in such object form - without including the above copyright and permission notices. - */ - -#ifndef FMT_FORMAT_H_ -#define FMT_FORMAT_H_ - -#include "core.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __clang__ -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else -# define FMT_CLANG_VERSION 0 -#endif - -#ifdef __INTEL_COMPILER -# define FMT_ICC_VERSION __INTEL_COMPILER -#elif defined(__ICL) -# define FMT_ICC_VERSION __ICL -#else -# define FMT_ICC_VERSION 0 -#endif - -#ifdef __NVCC__ -# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) -#else -# define FMT_CUDA_VERSION 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - -#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \ - (__cplusplus >= 201703 || FMT_GCC_VERSION != 0) -# define FMT_FALLTHROUGH [[fallthrough]] -#else -# define FMT_FALLTHROUGH -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# if FMT_MSC_VER -FMT_BEGIN_NAMESPACE -namespace internal { -template inline void do_throw(const Exception& x) { - // Silence unreachable code warnings in MSVC because these are nearly - // impossible to fix in a generic code. - volatile bool b = true; - if (b) throw x; -} -} // namespace internal -FMT_END_NAMESPACE -# define FMT_THROW(x) internal::do_throw(x) -# else -# define FMT_THROW(x) throw x -# endif -# else -# define FMT_THROW(x) \ - do { \ - static_cast(sizeof(x)); \ - FMT_ASSERT(false, ""); \ - } while (false) -# endif -#endif - -#ifndef FMT_USE_USER_DEFINED_LITERALS -// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ - (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \ - FMT_CUDA_VERSION >= 700) -# define FMT_USE_USER_DEFINED_LITERALS 1 -# else -# define FMT_USE_USER_DEFINED_LITERALS 0 -# endif -#endif - -#ifndef FMT_USE_UDL_TEMPLATE -// EDG front end based compilers (icc, nvcc) do not support UDL templates yet -// and GCC 9 warns about them. -# if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \ - FMT_CUDA_VERSION == 0 && \ - ((FMT_GCC_VERSION >= 600 && FMT_GCC_VERSION <= 900 && \ - __cplusplus >= 201402L) || \ - FMT_CLANG_VERSION >= 304) -# define FMT_USE_UDL_TEMPLATE 1 -# else -# define FMT_USE_UDL_TEMPLATE 0 -# endif -#endif - -// __builtin_clz is broken in clang with Microsoft CodeGen: -// https://github.com/fmtlib/fmt/issues/519 -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - -// Some compilers masquerade as both MSVC and GCC-likes or otherwise support -// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the -// MSVC intrinsics if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) -# include // _BitScanReverse, _BitScanReverse64 - -FMT_BEGIN_NAMESPACE -namespace internal { -// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# ifndef __clang__ -# pragma intrinsic(_BitScanReverse) -# endif -inline uint32_t clz(uint32_t x) { - unsigned long r = 0; - _BitScanReverse(&r, x); - - FMT_ASSERT(x != 0, ""); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -# pragma warning(suppress : 6102) - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) internal::clz(n) - -# if defined(_WIN64) && !defined(__clang__) -# pragma intrinsic(_BitScanReverse64) -# endif - -inline uint32_t clzll(uint64_t x) { - unsigned long r = 0; -# ifdef _WIN64 - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - - FMT_ASSERT(x != 0, ""); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -# pragma warning(suppress : 6102) - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) internal::clzll(n) -} // namespace internal -FMT_END_NAMESPACE -#endif - -// Enable the deprecated numeric alignment. -#ifndef FMT_NUMERIC_ALIGN -# define FMT_NUMERIC_ALIGN 1 -#endif - -// Enable the deprecated percent specifier. -#ifndef FMT_DEPRECATED_PERCENT -# define FMT_DEPRECATED_PERCENT 0 -#endif - -FMT_BEGIN_NAMESPACE -namespace internal { - -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template inline T const_check(T value) { return value; } - -// An equivalent of `*reinterpret_cast(&source)` that doesn't have -// undefined behavior (e.g. due to type aliasing). -// Example: uint64_t d = bit_cast(2.718); -template -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); - Dest dest; - std::memcpy(&dest, &source, sizeof(dest)); - return dest; -} - -inline bool is_big_endian() { - auto u = 1u; - struct bytes { - char data[sizeof(u)]; - }; - return bit_cast(u).data[0] == 0; -} - -// A fallback implementation of uintptr_t for systems that lack it. -struct fallback_uintptr { - unsigned char value[sizeof(void*)]; - - fallback_uintptr() = default; - explicit fallback_uintptr(const void* p) { - *this = bit_cast(p); - if (is_big_endian()) { - for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) - std::swap(value[i], value[j]); - } - } -}; -#ifdef UINTPTR_MAX -using uintptr_t = ::uintptr_t; -inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } -#else -using uintptr_t = fallback_uintptr; -inline fallback_uintptr to_uintptr(const void* p) { - return fallback_uintptr(p); -} -#endif - -// Returns the largest possible value for type T. Same as -// std::numeric_limits::max() but shorter and not affected by the max macro. -template constexpr T max_value() { - return (std::numeric_limits::max)(); -} -template constexpr int num_bits() { - return std::numeric_limits::digits; -} -template <> constexpr int num_bits() { - return static_cast(sizeof(void*) * - std::numeric_limits::digits); -} - -// An approximation of iterator_t for pre-C++20 systems. -template -using iterator_t = decltype(std::begin(std::declval())); - -// Detect the iterator category of *any* given type in a SFINAE-friendly way. -// Unfortunately, older implementations of std::iterator_traits are not safe -// for use in a SFINAE-context. -template -struct iterator_category : std::false_type {}; - -template struct iterator_category { - using type = std::random_access_iterator_tag; -}; - -template -struct iterator_category> { - using type = typename It::iterator_category; -}; - -// Detect if *any* given type models the OutputIterator concept. -template class is_output_iterator { - // Check for mutability because all iterator categories derived from - // std::input_iterator_tag *may* also meet the requirements of an - // OutputIterator, thereby falling into the category of 'mutable iterators' - // [iterator.requirements.general] clause 4. The compiler reveals this - // property only at the point of *actually dereferencing* the iterator! - template - static decltype(*(std::declval())) test(std::input_iterator_tag); - template static char& test(std::output_iterator_tag); - template static const char& test(...); - - using type = decltype(test(typename iterator_category::type{})); - - public: - static const bool value = !std::is_const>::value; -}; - -// A workaround for std::string not having mutable data() until C++17. -template inline Char* get_data(std::basic_string& s) { - return &s[0]; -} -template -inline typename Container::value_type* get_data(Container& c) { - return c.data(); -} - -#ifdef _SECURE_SCL -// Make a checked iterator to avoid MSVC warnings. -template using checked_ptr = stdext::checked_array_iterator; -template checked_ptr make_checked(T* p, std::size_t size) { - return {p, size}; -} -#else -template using checked_ptr = T*; -template inline T* make_checked(T* p, std::size_t) { return p; } -#endif - -template ::value)> -inline checked_ptr reserve( - std::back_insert_iterator& it, std::size_t n) { - Container& c = get_container(it); - std::size_t size = c.size(); - c.resize(size + n); - return make_checked(get_data(c) + size, n); -} - -template -inline Iterator& reserve(Iterator& it, std::size_t) { - return it; -} - -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - std::size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - using _Unchecked_type = counting_iterator; // Mark iterator as checked. - - struct value_type { - template void operator=(const T&) {} - }; - - counting_iterator() : count_(0) {} - - std::size_t count() const { return count_; } - - counting_iterator& operator++() { - ++count_; - return *this; - } - - counting_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - value_type operator*() const { return {}; } -}; - -template class truncating_iterator_base { - protected: - OutputIt out_; - std::size_t limit_; - std::size_t count_; - - truncating_iterator_base(OutputIt out, std::size_t limit) - : out_(out), limit_(limit), count_(0) {} - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = void; - using pointer = void; - using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. - - OutputIt base() const { return out_; } - std::size_t count() const { return count_; } -}; - -// An output iterator that truncates the output and counts the number of objects -// written to it. -template ::value_type>::type> -class truncating_iterator; - -template -class truncating_iterator - : public truncating_iterator_base { - using traits = std::iterator_traits; - - mutable typename traits::value_type blackhole_; - - public: - using value_type = typename traits::value_type; - - truncating_iterator(OutputIt out, std::size_t limit) - : truncating_iterator_base(out, limit) {} - - truncating_iterator& operator++() { - if (this->count_++ < this->limit_) ++this->out_; - return *this; - } - - truncating_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - value_type& operator*() const { - return this->count_ < this->limit_ ? *this->out_ : blackhole_; - } -}; - -template -class truncating_iterator - : public truncating_iterator_base { - public: - using value_type = typename OutputIt::container_type::value_type; - - truncating_iterator(OutputIt out, std::size_t limit) - : truncating_iterator_base(out, limit) {} - - truncating_iterator& operator=(value_type val) { - if (this->count_++ < this->limit_) this->out_ = val; - return *this; - } - - truncating_iterator& operator++() { return *this; } - truncating_iterator& operator++(int) { return *this; } - truncating_iterator& operator*() { return *this; } -}; - -// A range with the specified output iterator and value type. -template -class output_range { - private: - OutputIt it_; - - public: - using value_type = T; - using iterator = OutputIt; - struct sentinel {}; - - explicit output_range(OutputIt it) : it_(it) {} - OutputIt begin() const { return it_; } - sentinel end() const { return {}; } // Sentinel is not used yet. -}; - -template -inline size_t count_code_points(basic_string_view s) { - return s.size(); -} - -// Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { - const char8_t* data = s.data(); - size_t num_code_points = 0; - for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80) ++num_code_points; - } - return num_code_points; -} - -template -inline size_t code_point_index(basic_string_view s, size_t n) { - size_t size = s.size(); - return n < size ? n : size; -} - -// Calculates the index of the nth code point in a UTF-8 string. -inline size_t code_point_index(basic_string_view s, size_t n) { - const char8_t* data = s.data(); - size_t num_code_points = 0; - for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { - return i; - } - } - return s.size(); -} - -inline char8_t to_char8_t(char c) { return static_cast(c); } - -template -using needs_conversion = bool_constant< - std::is_same::value_type, - char>::value && - std::is_same::value>; - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::copy(begin, end, it); -} - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::transform(begin, end, it, to_char8_t); -} - -#ifndef FMT_USE_GRISU -# define FMT_USE_GRISU 1 -#endif - -template constexpr bool use_grisu() { - return FMT_USE_GRISU && std::numeric_limits::is_iec559 && - sizeof(T) <= sizeof(double); -} - -template -template -void buffer::append(const U* begin, const U* end) { - std::size_t new_size = size_ + to_unsigned(end - begin); - reserve(new_size); - std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_); - size_ = new_size; -} -} // namespace internal - -// A range with an iterator appending to a buffer. -template -class buffer_range : public internal::output_range< - std::back_insert_iterator>, T> { - public: - using iterator = std::back_insert_iterator>; - using internal::output_range::output_range; - buffer_range(internal::buffer& buf) - : internal::output_range(std::back_inserter(buf)) {} -}; - -// A UTF-8 string view. -class u8string_view : public basic_string_view { - public: - u8string_view(const char* s) - : basic_string_view(reinterpret_cast(s)) {} - u8string_view(const char* s, size_t count) FMT_NOEXCEPT - : basic_string_view(reinterpret_cast(s), count) { - } -}; - -#if FMT_USE_USER_DEFINED_LITERALS -inline namespace literals { -inline u8string_view operator"" _u(const char* s, std::size_t n) { - return {s, n}; -} -} // namespace literals -#endif - -// The number of characters to store in the basic_memory_buffer object itself -// to avoid dynamic memory allocation. -enum { inline_buffer_size = 500 }; - -/** - \rst - A dynamically growing memory buffer for trivially copyable/constructible types - with the first ``SIZE`` elements stored in the object itself. - - You can use one of the following type aliases for common character types: - - +----------------+------------------------------+ - | Type | Definition | - +================+==============================+ - | memory_buffer | basic_memory_buffer | - +----------------+------------------------------+ - | wmemory_buffer | basic_memory_buffer | - +----------------+------------------------------+ - - **Example**:: - - fmt::memory_buffer out; - format_to(out, "The answer is {}.", 42); - - This will append the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42. - - The output can be converted to an ``std::string`` with ``to_string(out)``. - \endrst - */ -template > -class basic_memory_buffer : private Allocator, public internal::buffer { - private: - T store_[SIZE]; - - // Deallocate memory allocated by the buffer. - void deallocate() { - T* data = this->data(); - if (data != store_) Allocator::deallocate(data, this->capacity()); - } - - protected: - void grow(std::size_t size) FMT_OVERRIDE; - - public: - using value_type = T; - using const_reference = const T&; - - explicit basic_memory_buffer(const Allocator& alloc = Allocator()) - : Allocator(alloc) { - this->set(store_, SIZE); - } - ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); } - - private: - // Move data from other to this buffer. - void move(basic_memory_buffer& other) { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - T* data = other.data(); - std::size_t size = other.size(), capacity = other.capacity(); - if (data == other.store_) { - this->set(store_, capacity); - std::uninitialized_copy(other.store_, other.store_ + size, - internal::make_checked(store_, capacity)); - } else { - this->set(data, capacity); - // Set pointer to the inline array so that delete is not called - // when deallocating. - other.set(other.store_, 0); - } - this->resize(size); - } - - public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ - basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } - - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ - basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { - FMT_ASSERT(this != &other, ""); - deallocate(); - move(other); - return *this; - } - - // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return *this; } -}; - -template -void basic_memory_buffer::grow(std::size_t size) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much"); -#endif - std::size_t old_capacity = this->capacity(); - std::size_t new_capacity = old_capacity + old_capacity / 2; - if (size > new_capacity) new_capacity = size; - T* old_data = this->data(); - T* new_data = std::allocator_traits::allocate(*this, new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy(old_data, old_data + this->size(), - internal::make_checked(new_data, new_capacity)); - this->set(new_data, new_capacity); - // deallocate must not throw according to the standard, but even if it does, - // the buffer already uses the new storage and will deallocate it in - // destructor. - if (old_data != store_) Allocator::deallocate(old_data, old_capacity); -} - -using memory_buffer = basic_memory_buffer; -using wmemory_buffer = basic_memory_buffer; - -/** A formatting error such as invalid format string. */ -class FMT_API format_error : public std::runtime_error { - public: - explicit format_error(const char* message) : std::runtime_error(message) {} - explicit format_error(const std::string& message) - : std::runtime_error(message) {} - format_error(const format_error&) = default; - format_error& operator=(const format_error&) = default; - format_error(format_error&&) = default; - format_error& operator=(format_error&&) = default; - ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; -}; - -namespace internal { - -// Returns true if value is negative, false otherwise. -// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. -template ::is_signed)> -FMT_CONSTEXPR bool is_negative(T value) { - return value < 0; -} -template ::is_signed)> -FMT_CONSTEXPR bool is_negative(T) { - return false; -} - -// Smallest of uint32_t, uint64_t, uint128_t that is large enough to -// represent all values of T. -template -using uint32_or_64_or_128_t = conditional_t< - std::numeric_limits::digits <= 32, uint32_t, - conditional_t::digits <= 64, uint64_t, uint128_t>>; - -// Static data is placed in this class template for the header-only config. -template struct FMT_EXTERN_TEMPLATE_API basic_data { - static const uint64_t powers_of_10_64[]; - static const uint32_t zero_or_powers_of_10_32[]; - static const uint64_t zero_or_powers_of_10_64[]; - static const uint64_t pow10_significands[]; - static const int16_t pow10_exponents[]; - static const char digits[]; - static const char hex_digits[]; - static const char foreground_color[]; - static const char background_color[]; - static const char reset_color[5]; - static const wchar_t wreset_color[5]; - static const char signs[]; -}; - -FMT_EXTERN template struct basic_data; - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct data : basic_data<> {}; - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline int count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return t - (n < data::zero_or_powers_of_10_64[t]) + 1; -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline int count_digits(uint64_t n) { - int count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000u; - count += 4; - } -} -#endif - -#if FMT_USE_INT128 -inline int count_digits(uint128_t n) { - int count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000U; - count += 4; - } -} -#endif - -// Counts the number of digits in n. BITS = log2(radix). -template inline int count_digits(UInt n) { - int num_digits = 0; - do { - ++num_digits; - } while ((n >>= BITS) != 0); - return num_digits; -} - -template <> int count_digits<4>(internal::fallback_uintptr n); - -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -#else -# define FMT_ALWAYS_INLINE -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline int count_digits(uint32_t n) { - int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return t - (n < data::zero_or_powers_of_10_32[t]) + 1; -} -#endif - -template FMT_API std::string grouping_impl(locale_ref loc); -template inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); -} -template <> inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); -} - -template FMT_API Char thousands_sep_impl(locale_ref loc); -template inline Char thousands_sep(locale_ref loc) { - return Char(thousands_sep_impl(loc)); -} -template <> inline wchar_t thousands_sep(locale_ref loc) { - return thousands_sep_impl(loc); -} - -template FMT_API Char decimal_point_impl(locale_ref loc); -template inline Char decimal_point(locale_ref loc) { - return Char(decimal_point_impl(loc)); -} -template <> inline wchar_t decimal_point(locale_ref loc) { - return decimal_point_impl(loc); -} - -// Formats a decimal unsigned integer value writing into buffer. -// add_thousands_sep is called after writing each char to add a thousands -// separator if necessary. -template -inline Char* format_decimal(Char* buffer, UInt value, int num_digits, - F add_thousands_sep) { - FMT_ASSERT(num_digits >= 0, "invalid digit count"); - buffer += num_digits; - Char* end = buffer; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - auto index = static_cast((value % 100) * 2); - value /= 100; - *--buffer = static_cast(data::digits[index + 1]); - add_thousands_sep(buffer); - *--buffer = static_cast(data::digits[index]); - add_thousands_sep(buffer); - } - if (value < 10) { - *--buffer = static_cast('0' + value); - return end; - } - auto index = static_cast(value * 2); - *--buffer = static_cast(data::digits[index + 1]); - add_thousands_sep(buffer); - *--buffer = static_cast(data::digits[index]); - return end; -} - -template constexpr int digits10() noexcept { - return std::numeric_limits::digits10; -} -template <> constexpr int digits10() noexcept { return 38; } -template <> constexpr int digits10() noexcept { return 38; } - -template -inline Iterator format_decimal(Iterator out, UInt value, int num_digits, - F add_thousands_sep) { - FMT_ASSERT(num_digits >= 0, "invalid digit count"); - // Buffer should be large enough to hold all digits (<= digits10 + 1). - enum { max_size = digits10() + 1 }; - Char buffer[2 * max_size]; - auto end = format_decimal(buffer, value, num_digits, add_thousands_sep); - return internal::copy_str(buffer, end, out); -} - -template -inline It format_decimal(It out, UInt value, int num_digits) { - return format_decimal(out, value, num_digits, [](Char*) {}); -} - -template -inline Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { - buffer += num_digits; - Char* end = buffer; - do { - const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; - unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); - } while ((value >>= BASE_BITS) != 0); - return end; -} - -template -Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits, - bool = false) { - auto char_digits = std::numeric_limits::digits / 4; - int start = (num_digits + char_digits - 1) / char_digits - 1; - if (int start_digits = num_digits % char_digits) { - unsigned value = n.value[start--]; - buffer = format_uint(buffer, value, start_digits); - } - for (; start >= 0; --start) { - unsigned value = n.value[start]; - buffer += char_digits; - auto p = buffer; - for (int i = 0; i < char_digits; ++i) { - unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--p = static_cast(data::hex_digits[digit]); - value >>= BASE_BITS; - } - } - return buffer; -} - -template -inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { - // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1]; - format_uint(buffer, value, num_digits, upper); - return internal::copy_str(buffer, buffer + num_digits, out); -} - -#ifndef _WIN32 -# define FMT_USE_WINDOWS_H 0 -#elif !defined(FMT_USE_WINDOWS_H) -# define FMT_USE_WINDOWS_H 1 -#endif - -// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. -// All the functionality that relies on it will be disabled too. -#if FMT_USE_WINDOWS_H -// A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class utf8_to_utf16 { - private: - wmemory_buffer buffer_; - - public: - FMT_API explicit utf8_to_utf16(string_view s); - operator wstring_view() const { return wstring_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const wchar_t* c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } -}; - -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class utf16_to_utf8 { - private: - memory_buffer buffer_; - - public: - utf16_to_utf8() {} - FMT_API explicit utf16_to_utf8(wstring_view s); - operator string_view() const { return string_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char* c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - FMT_API int convert(wstring_view s); -}; - -FMT_API void format_windows_error(internal::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT; -#endif - -template struct null {}; - -// Workaround an array initialization issue in gcc 4.8. -template struct fill_t { - private: - Char data_[6]; - - public: - FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } - FMT_CONSTEXPR const Char& operator[](size_t index) const { - return data_[index]; - } - - static FMT_CONSTEXPR fill_t make() { - auto fill = fill_t(); - fill[0] = Char(' '); - return fill; - } -}; -} // namespace internal - -// We cannot use enum classes as bit fields because of a gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. -namespace align { -enum type { none, left, right, center, numeric }; -} -using align_t = align::type; - -namespace sign { -enum type { none, minus, plus, space }; -} -using sign_t = sign::type; - -// Format specifiers for built-in and string types. -template struct basic_format_specs { - int width; - int precision; - char type; - align_t align : 4; - sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). - internal::fill_t fill; - - constexpr basic_format_specs() - : width(0), - precision(-1), - type(0), - align(align::none), - sign(sign::none), - alt(false), - fill(internal::fill_t::make()) {} -}; - -using format_specs = basic_format_specs; - -namespace internal { - -// A floating-point presentation format. -enum class float_format : unsigned char { - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed, // Fixed point with the default precision of 6, e.g. 0.0012. - hex -}; - -struct float_specs { - int precision; - float_format format : 8; - sign_t sign : 8; - bool upper : 1; - bool locale : 1; - bool percent : 1; - bool binary32 : 1; - bool use_grisu : 1; - bool trailing_zeros : 1; -}; - -// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template It write_exponent(int exp, It it) { - FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); - if (exp < 0) { - *it++ = static_cast('-'); - exp = -exp; - } else { - *it++ = static_cast('+'); - } - if (exp >= 100) { - const char* top = data::digits + (exp / 100) * 2; - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; - } - const char* d = data::digits + exp * 2; - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; -} - -template class float_writer { - private: - // The number is given as v = digits_ * pow(10, exp_). - const char* digits_; - int num_digits_; - int exp_; - size_t size_; - float_specs specs_; - Char decimal_point_; - - template It prettify(It it) const { - // pow(10, full_exp - 1) <= v <= pow(10, full_exp). - int full_exp = num_digits_ + exp_; - if (specs_.format == float_format::exp) { - // Insert a decimal point after the first digit and add an exponent. - *it++ = static_cast(*digits_); - int num_zeros = specs_.precision - num_digits_; - bool trailing_zeros = num_zeros > 0 && specs_.trailing_zeros; - if (num_digits_ > 1 || trailing_zeros) *it++ = decimal_point_; - it = copy_str(digits_ + 1, digits_ + num_digits_, it); - if (trailing_zeros) - it = std::fill_n(it, num_zeros, static_cast('0')); - *it++ = static_cast(specs_.upper ? 'E' : 'e'); - return write_exponent(full_exp - 1, it); - } - if (num_digits_ <= full_exp) { - // 1234e7 -> 12340000000[.0+] - it = copy_str(digits_, digits_ + num_digits_, it); - it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); - if (specs_.trailing_zeros) { - *it++ = decimal_point_; - int num_zeros = specs_.precision - full_exp; - if (num_zeros <= 0) { - if (specs_.format != float_format::fixed) - *it++ = static_cast('0'); - return it; - } -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (num_zeros > 1000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif - it = std::fill_n(it, num_zeros, static_cast('0')); - } - } else if (full_exp > 0) { - // 1234e-2 -> 12.34[0+] - it = copy_str(digits_, digits_ + full_exp, it); - if (!specs_.trailing_zeros) { - // Remove trailing zeros. - int num_digits = num_digits_; - while (num_digits > full_exp && digits_[num_digits - 1] == '0') - --num_digits; - if (num_digits != full_exp) *it++ = decimal_point_; - return copy_str(digits_ + full_exp, digits_ + num_digits, it); - } - *it++ = decimal_point_; - it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); - if (specs_.precision > num_digits_) { - // Add trailing zeros. - int num_zeros = specs_.precision - num_digits_; - it = std::fill_n(it, num_zeros, static_cast('0')); - } - } else { - // 1234e-6 -> 0.001234 - *it++ = static_cast('0'); - int num_zeros = -full_exp; - if (specs_.precision >= 0 && specs_.precision < num_zeros) - num_zeros = specs_.precision; - int num_digits = num_digits_; - if (!specs_.trailing_zeros) - while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; - if (num_zeros != 0 || num_digits != 0) { - *it++ = decimal_point_; - it = std::fill_n(it, num_zeros, static_cast('0')); - it = copy_str(digits_, digits_ + num_digits, it); - } - } - return it; - } - - public: - float_writer(const char* digits, int num_digits, int exp, float_specs specs, - Char decimal_point) - : digits_(digits), - num_digits_(num_digits), - exp_(exp), - specs_(specs), - decimal_point_(decimal_point) { - int full_exp = num_digits + exp - 1; - int precision = specs.precision > 0 ? specs.precision : 16; - if (specs_.format == float_format::general && - !(full_exp >= -4 && full_exp < precision)) { - specs_.format = float_format::exp; - } - size_ = prettify(counting_iterator()).count(); - size_ += specs.sign ? 1 : 0; - } - - size_t size() const { return size_; } - size_t width() const { return size(); } - - template void operator()(It&& it) { - if (specs_.sign) *it++ = static_cast(data::signs[specs_.sign]); - it = prettify(it); - } -}; - -template -int format_float(T value, int precision, float_specs specs, buffer& buf); - -// Formats a floating-point number with snprintf. -template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf); - -template T promote_float(T value) { return value; } -inline double promote_float(float value) { return value; } - -template -FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { - switch (spec) { - case 0: - case 'd': - handler.on_dec(); - break; - case 'x': - case 'X': - handler.on_hex(); - break; - case 'b': - case 'B': - handler.on_bin(); - break; - case 'o': - handler.on_oct(); - break; - case 'n': - handler.on_num(); - break; - default: - handler.on_error(); - } -} - -template -FMT_CONSTEXPR float_specs parse_float_type_spec( - const basic_format_specs& specs, ErrorHandler&& eh = {}) { - auto result = float_specs(); - result.trailing_zeros = specs.alt; - switch (specs.type) { - case 0: - result.format = float_format::general; - result.trailing_zeros |= specs.precision != 0; - break; - case 'G': - result.upper = true; - FMT_FALLTHROUGH; - case 'g': - result.format = float_format::general; - break; - case 'E': - result.upper = true; - FMT_FALLTHROUGH; - case 'e': - result.format = float_format::exp; - result.trailing_zeros |= specs.precision != 0; - break; - case 'F': - result.upper = true; - FMT_FALLTHROUGH; - case 'f': - result.format = float_format::fixed; - result.trailing_zeros |= specs.precision != 0; - break; -#if FMT_DEPRECATED_PERCENT - case '%': - result.format = float_format::fixed; - result.percent = true; - break; -#endif - case 'A': - result.upper = true; - FMT_FALLTHROUGH; - case 'a': - result.format = float_format::hex; - break; - case 'n': - result.locale = true; - break; - default: - eh.on_error("invalid type specifier"); - break; - } - return result; -} - -template -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, - Handler&& handler) { - if (!specs) return handler.on_char(); - if (specs->type && specs->type != 'c') return handler.on_int(); - if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) - handler.on_error("invalid format specifier for char"); - handler.on_char(); -} - -template -FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { - if (spec == 0 || spec == 's') - handler.on_string(); - else if (spec == 'p') - handler.on_pointer(); - else - handler.on_error("invalid type specifier"); -} - -template -FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); -} - -template -FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); -} - -template class int_type_checker : private ErrorHandler { - public: - FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} - - FMT_CONSTEXPR void on_dec() {} - FMT_CONSTEXPR void on_hex() {} - FMT_CONSTEXPR void on_bin() {} - FMT_CONSTEXPR void on_oct() {} - FMT_CONSTEXPR void on_num() {} - - FMT_CONSTEXPR void on_error() { - ErrorHandler::on_error("invalid type specifier"); - } -}; - -template -class char_specs_checker : public ErrorHandler { - private: - char type_; - - public: - FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) - : ErrorHandler(eh), type_(type) {} - - FMT_CONSTEXPR void on_int() { - handle_int_type_spec(type_, int_type_checker(*this)); - } - FMT_CONSTEXPR void on_char() {} -}; - -template -class cstring_type_checker : public ErrorHandler { - public: - FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) - : ErrorHandler(eh) {} - - FMT_CONSTEXPR void on_string() {} - FMT_CONSTEXPR void on_pointer() {} -}; - -template -void arg_map::init(const basic_format_args& args) { - if (map_) return; - map_ = new entry[internal::to_unsigned(args.max_size())]; - if (args.is_packed()) { - for (int i = 0;; ++i) { - internal::type arg_type = args.type(i); - if (arg_type == internal::none_type) return; - if (arg_type == internal::named_arg_type) push_back(args.values_[i]); - } - } - for (int i = 0, n = args.max_size(); i < n; ++i) { - auto type = args.args_[i].type_; - if (type == internal::named_arg_type) push_back(args.args_[i].value_); - } -} - -template struct nonfinite_writer { - sign_t sign; - const char* str; - static constexpr size_t str_size = 3; - - size_t size() const { return str_size + (sign ? 1 : 0); } - size_t width() const { return size(); } - - template void operator()(It&& it) const { - if (sign) *it++ = static_cast(data::signs[sign]); - it = copy_str(str, str + str_size, it); - } -}; - -// This template provides operations for formatting and writing data into a -// character range. -template class basic_writer { - public: - using char_type = typename Range::value_type; - using iterator = typename Range::iterator; - using format_specs = basic_format_specs; - - private: - iterator out_; // Output iterator. - locale_ref locale_; - - // Attempts to reserve space for n extra characters in the output range. - // Returns a pointer to the reserved range or a reference to out_. - auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) { - return internal::reserve(out_, n); - } - - template struct padded_int_writer { - size_t size_; - string_view prefix; - char_type fill; - std::size_t padding; - F f; - - size_t size() const { return size_; } - size_t width() const { return size_; } - - template void operator()(It&& it) const { - if (prefix.size() != 0) - it = copy_str(prefix.begin(), prefix.end(), it); - it = std::fill_n(it, padding, fill); - f(it); - } - }; - - // Writes an integer in the format - // - // where are written by f(it). - template - void write_int(int num_digits, string_view prefix, format_specs specs, F f) { - std::size_t size = prefix.size() + to_unsigned(num_digits); - char_type fill = specs.fill[0]; - std::size_t padding = 0; - if (specs.align == align::numeric) { - auto unsiged_width = to_unsigned(specs.width); - if (unsiged_width > size) { - padding = unsiged_width - size; - size = unsiged_width; - } - } else if (specs.precision > num_digits) { - size = prefix.size() + to_unsigned(specs.precision); - padding = to_unsigned(specs.precision - num_digits); - fill = static_cast('0'); - } - if (specs.align == align::none) specs.align = align::right; - write_padded(specs, padded_int_writer{size, prefix, fill, padding, f}); - } - - // Writes a decimal integer. - template void write_decimal(Int value) { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto&& it = reserve((negative ? 1 : 0) + static_cast(num_digits)); - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits); - } - - // The handle_int_type_spec handler that writes an integer. - template struct int_writer { - using unsigned_type = uint32_or_64_or_128_t; - - basic_writer& writer; - const Specs& specs; - unsigned_type abs_value; - char prefix[4]; - unsigned prefix_size; - - string_view get_prefix() const { return string_view(prefix, prefix_size); } - - int_writer(basic_writer& w, Int value, const Specs& s) - : writer(w), - specs(s), - abs_value(static_cast(value)), - prefix_size(0) { - if (is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (specs.sign != sign::none && specs.sign != sign::minus) { - prefix[0] = specs.sign == sign::plus ? '+' : ' '; - ++prefix_size; - } - } - - struct dec_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = internal::format_decimal(it, abs_value, num_digits); - } - }; - - void on_dec() { - int num_digits = count_digits(abs_value); - writer.write_int(num_digits, get_prefix(), specs, - dec_writer{abs_value, num_digits}); - } - - struct hex_writer { - int_writer& self; - int num_digits; - - template void operator()(It&& it) const { - it = format_uint<4, char_type>(it, self.abs_value, num_digits, - self.specs.type != 'x'); - } - }; - - void on_hex() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = specs.type; - } - int num_digits = count_digits<4>(abs_value); - writer.write_int(num_digits, get_prefix(), specs, - hex_writer{*this, num_digits}); - } - - template struct bin_writer { - unsigned_type abs_value; - int num_digits; - - template void operator()(It&& it) const { - it = format_uint(it, abs_value, num_digits); - } - }; - - void on_bin() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(specs.type); - } - int num_digits = count_digits<1>(abs_value); - writer.write_int(num_digits, get_prefix(), specs, - bin_writer<1>{abs_value, num_digits}); - } - - void on_oct() { - int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix[prefix_size++] = '0'; - } - writer.write_int(num_digits, get_prefix(), specs, - bin_writer<3>{abs_value, num_digits}); - } - - enum { sep_size = 1 }; - - struct num_writer { - unsigned_type abs_value; - int size; - const std::string& groups; - char_type sep; - - template void operator()(It&& it) const { - basic_string_view s(&sep, sep_size); - // Index of a decimal digit with the least significant digit having - // index 0. - int digit_index = 0; - std::string::const_iterator group = groups.cbegin(); - it = format_decimal( - it, abs_value, size, - [this, s, &group, &digit_index](char_type*& buffer) { - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - return; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; - } - buffer -= s.size(); - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(buffer, s.size())); - }); - } - }; - - void on_num() { - std::string groups = grouping(writer.locale_); - if (groups.empty()) return on_dec(); - auto sep = thousands_sep(writer.locale_); - if (!sep) return on_dec(); - int num_digits = count_digits(abs_value); - int size = num_digits; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && num_digits > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - num_digits -= *group; - ++group; - } - if (group == groups.cend()) - size += sep_size * ((num_digits - 1) / groups.back()); - writer.write_int(size, get_prefix(), specs, - num_writer{abs_value, size, groups, sep}); - } - - FMT_NORETURN void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } - }; - - template struct str_writer { - const Char* s; - size_t size_; - - size_t size() const { return size_; } - size_t width() const { - return count_code_points(basic_string_view(s, size_)); - } - - template void operator()(It&& it) const { - it = copy_str(s, s + size_, it); - } - }; - - template struct pointer_writer { - UIntPtr value; - int num_digits; - - size_t size() const { return to_unsigned(num_digits) + 2; } - size_t width() const { return size(); } - - template void operator()(It&& it) const { - *it++ = static_cast('0'); - *it++ = static_cast('x'); - it = format_uint<4, char_type>(it, value, num_digits); - } - }; - - public: - explicit basic_writer(Range out, locale_ref loc = locale_ref()) - : out_(out.begin()), locale_(loc) {} - - iterator out() const { return out_; } - - // Writes a value in the format - // - // where is written by f(it). - template void write_padded(const format_specs& specs, F&& f) { - // User-perceived width (in code points). - unsigned width = to_unsigned(specs.width); - size_t size = f.size(); // The number of code units. - size_t num_code_points = width != 0 ? f.width() : size; - if (width <= num_code_points) return f(reserve(size)); - auto&& it = reserve(width + (size - num_code_points)); - char_type fill = specs.fill[0]; - std::size_t padding = width - num_code_points; - if (specs.align == align::right) { - it = std::fill_n(it, padding, fill); - f(it); - } else if (specs.align == align::center) { - std::size_t left_padding = padding / 2; - it = std::fill_n(it, left_padding, fill); - f(it); - it = std::fill_n(it, padding - left_padding, fill); - } else { - f(it); - it = std::fill_n(it, padding, fill); - } - } - - void write(int value) { write_decimal(value); } - void write(long value) { write_decimal(value); } - void write(long long value) { write_decimal(value); } - - void write(unsigned value) { write_decimal(value); } - void write(unsigned long value) { write_decimal(value); } - void write(unsigned long long value) { write_decimal(value); } - -#if FMT_USE_INT128 - void write(int128_t value) { write_decimal(value); } - void write(uint128_t value) { write_decimal(value); } -#endif - - template - void write_int(T value, const Spec& spec) { - handle_int_type_spec(spec.type, int_writer(*this, value, spec)); - } - - template ::value)> - void write(T value, format_specs specs = {}) { - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; - if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. - fspecs.sign = sign::minus; - value = -value; - } else if (fspecs.sign == sign::minus) { - fspecs.sign = sign::none; - } - - if (!std::isfinite(value)) { - auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf") - : (fspecs.upper ? "NAN" : "nan"); - return write_padded(specs, nonfinite_writer{fspecs.sign, str}); - } - - if (specs.align == align::none) { - specs.align = align::right; - } else if (specs.align == align::numeric) { - if (fspecs.sign) { - auto&& it = reserve(1); - *it++ = static_cast(data::signs[fspecs.sign]); - fspecs.sign = sign::none; - if (specs.width != 0) --specs.width; - } - specs.align = align::right; - } - - memory_buffer buffer; - if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); - snprintf_float(promote_float(value), specs.precision, fspecs, buffer); - write_padded(specs, str_writer{buffer.data(), buffer.size()}); - return; - } - int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; - if (fspecs.format == float_format::exp) ++precision; - if (const_check(std::is_same())) fspecs.binary32 = true; - fspecs.use_grisu = use_grisu(); - if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100; - int exp = format_float(promote_float(value), precision, fspecs, buffer); - if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) { - buffer.push_back('%'); - --exp; // Adjust decimal place position. - } - fspecs.precision = precision; - char_type point = fspecs.locale ? decimal_point(locale_) - : static_cast('.'); - write_padded(specs, float_writer(buffer.data(), - static_cast(buffer.size()), - exp, fspecs, point)); - } - - void write(char value) { - auto&& it = reserve(1); - *it++ = value; - } - - template ::value)> - void write(Char value) { - auto&& it = reserve(1); - *it++ = value; - } - - void write(string_view value) { - auto&& it = reserve(value.size()); - it = copy_str(value.begin(), value.end(), it); - } - void write(wstring_view value) { - static_assert(std::is_same::value, ""); - auto&& it = reserve(value.size()); - it = std::copy(value.begin(), value.end(), it); - } - - template - void write(const Char* s, std::size_t size, const format_specs& specs) { - write_padded(specs, str_writer{s, size}); - } - - template - void write(basic_string_view s, const format_specs& specs = {}) { - const Char* data = s.data(); - std::size_t size = s.size(); - if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); - write(data, size, specs); - } - - template - void write_pointer(UIntPtr value, const format_specs* specs) { - int num_digits = count_digits<4>(value); - auto pw = pointer_writer{value, num_digits}; - if (!specs) return pw(reserve(to_unsigned(num_digits) + 2)); - format_specs specs_copy = *specs; - if (specs_copy.align == align::none) specs_copy.align = align::right; - write_padded(specs_copy, pw); - } -}; - -using writer = basic_writer>; - -template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; -template <> struct is_integral : std::true_type {}; - -template -class arg_formatter_base { - public: - using char_type = typename Range::value_type; - using iterator = typename Range::iterator; - using format_specs = basic_format_specs; - - private: - using writer_type = basic_writer; - writer_type writer_; - format_specs* specs_; - - struct char_writer { - char_type value; - - size_t size() const { return 1; } - size_t width() const { return 1; } - - template void operator()(It&& it) const { *it++ = value; } - }; - - void write_char(char_type value) { - if (specs_) - writer_.write_padded(*specs_, char_writer{value}); - else - writer_.write(value); - } - - void write_pointer(const void* p) { - writer_.write_pointer(internal::to_uintptr(p), specs_); - } - - protected: - writer_type& writer() { return writer_; } - FMT_DEPRECATED format_specs* spec() { return specs_; } - format_specs* specs() { return specs_; } - iterator out() { return writer_.out(); } - - void write(bool value) { - string_view sv(value ? "true" : "false"); - specs_ ? writer_.write(sv, *specs_) : writer_.write(sv); - } - - void write(const char_type* value) { - if (!value) { - FMT_THROW(format_error("string pointer is null")); - } else { - auto length = std::char_traits::length(value); - basic_string_view sv(value, length); - specs_ ? writer_.write(sv, *specs_) : writer_.write(sv); - } - } - - public: - arg_formatter_base(Range r, format_specs* s, locale_ref loc) - : writer_(r, loc), specs_(s) {} - - iterator operator()(monostate) { - FMT_ASSERT(false, "invalid argument type"); - return out(); - } - - template ::value)> - iterator operator()(T value) { - if (specs_) - writer_.write_int(value, *specs_); - else - writer_.write(value); - return out(); - } - - iterator operator()(char_type value) { - internal::handle_char_specs( - specs_, char_spec_handler(*this, static_cast(value))); - return out(); - } - - iterator operator()(bool value) { - if (specs_ && specs_->type) return (*this)(value ? 1 : 0); - write(value != 0); - return out(); - } - - template ::value)> - iterator operator()(T value) { - writer_.write(value, specs_ ? *specs_ : format_specs()); - return out(); - } - - struct char_spec_handler : ErrorHandler { - arg_formatter_base& formatter; - char_type value; - - char_spec_handler(arg_formatter_base& f, char_type val) - : formatter(f), value(val) {} - - void on_int() { - if (formatter.specs_) - formatter.writer_.write_int(value, *formatter.specs_); - else - formatter.writer_.write(value); - } - void on_char() { formatter.write_char(value); } - }; - - struct cstring_spec_handler : internal::error_handler { - arg_formatter_base& formatter; - const char_type* value; - - cstring_spec_handler(arg_formatter_base& f, const char_type* val) - : formatter(f), value(val) {} - - void on_string() { formatter.write(value); } - void on_pointer() { formatter.write_pointer(value); } - }; - - iterator operator()(const char_type* value) { - if (!specs_) return write(value), out(); - internal::handle_cstring_type_spec(specs_->type, - cstring_spec_handler(*this, value)); - return out(); - } - - iterator operator()(basic_string_view value) { - if (specs_) { - internal::check_string_type_spec(specs_->type, internal::error_handler()); - writer_.write(value, *specs_); - } else { - writer_.write(value); - } - return out(); - } - - iterator operator()(const void* value) { - if (specs_) - check_pointer_type_spec(specs_->type, internal::error_handler()); - write_pointer(value); - return out(); - } -}; - -template FMT_CONSTEXPR bool is_name_start(Char c) { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; -} - -// Parses the range [begin, end) as an unsigned integer. This function assumes -// that the range is non-empty and the first character is a digit. -template -FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, - ErrorHandler&& eh) { - FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - if (*begin == '0') { - ++begin; - return 0; - } - unsigned value = 0; - // Convert to unsigned to prevent a warning. - constexpr unsigned max_int = max_value(); - unsigned big = max_int / 10; - do { - // Check for overflow. - if (value > big) { - value = max_int + 1; - break; - } - value = value * 10 + unsigned(*begin - '0'); - ++begin; - } while (begin != end && '0' <= *begin && *begin <= '9'); - if (value > max_int) eh.on_error("number is too big"); - return static_cast(value); -} - -template class custom_formatter { - private: - using char_type = typename Context::char_type; - - basic_format_parse_context& parse_ctx_; - Context& ctx_; - - public: - explicit custom_formatter(basic_format_parse_context& parse_ctx, - Context& ctx) - : parse_ctx_(parse_ctx), ctx_(ctx) {} - - bool operator()(typename basic_format_arg::handle h) const { - h.format(parse_ctx_, ctx_); - return true; - } - - template bool operator()(T) const { return false; } -}; - -template -using is_integer = - bool_constant::value && !std::is_same::value && - !std::is_same::value && - !std::is_same::value>; - -template class width_checker { - public: - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { - if (is_negative(value)) handler_.on_error("negative width"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { - handler_.on_error("width is not integer"); - return 0; - } - - private: - ErrorHandler& handler_; -}; - -template class precision_checker { - public: - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { - if (is_negative(value)) handler_.on_error("negative precision"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { - handler_.on_error("precision is not integer"); - return 0; - } - - private: - ErrorHandler& handler_; -}; - -// A format specifier handler that sets fields in basic_format_specs. -template class specs_setter { - public: - explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) - : specs_(specs) {} - - FMT_CONSTEXPR specs_setter(const specs_setter& other) - : specs_(other.specs_) {} - - FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } - FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill[0] = fill; } - FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } - FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } - FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } - FMT_CONSTEXPR void on_hash() { specs_.alt = true; } - - FMT_CONSTEXPR void on_zero() { - specs_.align = align::numeric; - specs_.fill[0] = Char('0'); - } - - FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } - FMT_CONSTEXPR void on_precision(int precision) { - specs_.precision = precision; - } - FMT_CONSTEXPR void end_precision() {} - - FMT_CONSTEXPR void on_type(Char type) { - specs_.type = static_cast(type); - } - - protected: - basic_format_specs& specs_; -}; - -template class numeric_specs_checker { - public: - FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type) - : error_handler_(eh), arg_type_(arg_type) {} - - FMT_CONSTEXPR void require_numeric_argument() { - if (!is_arithmetic_type(arg_type_)) - error_handler_.on_error("format specifier requires numeric argument"); - } - - FMT_CONSTEXPR void check_sign() { - require_numeric_argument(); - if (is_integral_type(arg_type_) && arg_type_ != int_type && - arg_type_ != long_long_type && arg_type_ != internal::char_type) { - error_handler_.on_error("format specifier requires signed argument"); - } - } - - FMT_CONSTEXPR void check_precision() { - if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type) - error_handler_.on_error("precision not allowed for this argument type"); - } - - private: - ErrorHandler& error_handler_; - internal::type arg_type_; -}; - -// A format specifier handler that checks if specifiers are consistent with the -// argument type. -template class specs_checker : public Handler { - public: - FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type) - : Handler(handler), checker_(*this, arg_type) {} - - FMT_CONSTEXPR specs_checker(const specs_checker& other) - : Handler(other), checker_(*this, other.arg_type_) {} - - FMT_CONSTEXPR void on_align(align_t align) { - if (align == align::numeric) checker_.require_numeric_argument(); - Handler::on_align(align); - } - - FMT_CONSTEXPR void on_plus() { - checker_.check_sign(); - Handler::on_plus(); - } - - FMT_CONSTEXPR void on_minus() { - checker_.check_sign(); - Handler::on_minus(); - } - - FMT_CONSTEXPR void on_space() { - checker_.check_sign(); - Handler::on_space(); - } - - FMT_CONSTEXPR void on_hash() { - checker_.require_numeric_argument(); - Handler::on_hash(); - } - - FMT_CONSTEXPR void on_zero() { - checker_.require_numeric_argument(); - Handler::on_zero(); - } - - FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } - - private: - numeric_specs_checker checker_; -}; - -template