diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..d81061ddb23 --- /dev/null +++ b/.clang-format @@ -0,0 +1,101 @@ +# Style tries to match doxygen's source code style (not perfect) + +BasedOnStyle: LLVM + +# Do not reflow lines or comments; keeps existing wrapping/spacing. +ColumnLimit: 0 +ReflowComments: false + +# Preserve include order and grouping exactly as written. +SortIncludes: Never +IncludeBlocks: Preserve +SortUsingDeclarations: false + +# Indentation +UseTab: Never +IndentWidth: 2 +ContinuationIndentWidth: 2 +NamespaceIndentation: None +IndentPPDirectives: None +IndentCaseLabels: false +IndentGotoLabels: false +IndentAccessModifiers : true + +# Braces: Allman style (brace on next line for functions, classes, control). +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true + +# Access specifiers are indented like members in the file. +AccessModifierOffset: 0 +EmptyLineBeforeAccessModifier: Never + +# Short things on one line are common (e.g., inline ctor bodies). +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortLambdasOnASingleLine: All +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true + +# Initializer lists +BreakConstructorInitializers: AfterColon +AllowAllConstructorInitializersOnNextLine: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false + +# Spacing +SpaceBeforeParens: ControlStatements +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpacesInAngles: false +SpaceBeforeSquareBrackets: false +SpaceAfterCStyleCast: false + +# Pointer/reference binding matches code: `Type *name`, `Type &name`. +DerivePointerAlignment: false +PointerAlignment: Right +ReferenceAlignment: Right + +# Keep existing alignment/columns where possible. +AlignTrailingComments: true +AlignOperands: true +AlignConsecutiveAssignments: AcrossEmptyLinesAndComments +AlignConsecutiveDeclarations: AcrossEmptyLinesAndComments +AlignConsecutiveMacros: None +AlignEscapedNewlines: DontAlign +AlignAfterOpenBracket: Align +BreakBeforeBinaryOperators: None + +# Keep moderate vertical spacing, don't aggressively collapse empties. +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 + +# Avoid changing braced-init formatting in ways that could churn. +Cpp11BracedListStyle: false + +# Avoid messing with some macros +WhitespaceSensitiveMacros: + - SRCLANGEXT + - ML_TYPE + - TSPEC0 + - TSPEC + - ETYPE + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000000..1518459c01c --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,11 @@ +Checks: > + -*, + bugprone-use-after-move, + bugprone-infinite-loop, + bugprone-narrowing-conversions, + clang-analyzer-deadcode.DeadStores, + cppcoreguidelines-pro-type-cstyle-cast, + performance-unnecessary-copy-initialization, + performance-for-range-copy, + performance-move-const-arg, + portability-* diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f..2cb7271ea27 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,4 +3,6 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "daily" + time: "03:00" + open-pull-requests-limit: 3 diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 66f7a45f642..199db20820b 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -14,57 +14,86 @@ jobs: matrix: config: - { - name: "Ubuntu Latest GCC Release", - os: ubuntu-20.04, + name: "Ubuntu Latest GCC Release (Intel)", + os: ubuntu-24.04, build_type: "Release", cc: "gcc", cxx: "g++", build_gen: "Unix Makefiles", cmake_extra_opts: "-Dbuild_search=YES -Dbuild_app=YES -Dbuild_parse=YES" } - { - name: "Ubuntu Latest GCC Debug", - os: ubuntu-20.04, + name: "Ubuntu Latest GCC Debug (Intel)", + os: ubuntu-24.04, build_type: "Debug", cc: "gcc", cxx: "g++", build_gen: "Unix Makefiles", cmake_extra_opts: "-Dbuild_search=YES -Dbuild_app=YES -Dbuild_parse=YES" } - { - name: "Ubuntu Latest Clang Release", - os: ubuntu-20.04, + name: "Ubuntu Latest Clang Release (Intel)", + os: ubuntu-24.04, build_type: "Release", cc: "clang", cxx: "clang++", build_gen: "Unix Makefiles", cmake_extra_opts: "-Duse_libclang=YES -Dstatic_libclang=YES -Duse_libc++=NO" } - { - name: "Ubuntu Latest Clang Debug", - os: ubuntu-20.04, + name: "Ubuntu Latest Clang Debug (Intel)", + os: ubuntu-24.04, + build_type: "Debug", cc: "clang", cxx: "clang++", + build_gen: "Unix Makefiles", + cmake_extra_opts: "-Duse_libclang=YES -Dstatic_libclang=YES -Duse_libc++=NO" + } + + - { + name: "Ubuntu Latest GCC Release (Arm)", + os: ubuntu-24.04-arm, + build_type: "Release", cc: "gcc", cxx: "g++", + build_gen: "Unix Makefiles", + cmake_extra_opts: "-Dbuild_search=YES -Dbuild_app=YES -Dbuild_parse=YES" + } + - { + name: "Ubuntu Latest GCC Debug (Arm)", + os: ubuntu-24.04-arm, + build_type: "Debug", cc: "gcc", cxx: "g++", + build_gen: "Unix Makefiles", + cmake_extra_opts: "-Dbuild_search=YES -Dbuild_app=YES -Dbuild_parse=YES" + } + - { + name: "Ubuntu Latest Clang Release (Arm)", + os: ubuntu-24.04-arm, + build_type: "Release", cc: "clang", cxx: "clang++", + build_gen: "Unix Makefiles", + cmake_extra_opts: "-Duse_libclang=YES -Dstatic_libclang=YES -Duse_libc++=NO" + } + - { + name: "Ubuntu Latest Clang Debug (Arm)", + os: ubuntu-24.04-arm, build_type: "Debug", cc: "clang", cxx: "clang++", build_gen: "Unix Makefiles", cmake_extra_opts: "-Duse_libclang=YES -Dstatic_libclang=YES -Duse_libc++=NO" } - { name: "macOS Latest Release (Intel)", - os: macos-13, + os: macos-15-intel, build_type: "Release", cc: "clang", cxx: "clang++", build_gen: "Unix Makefiles" } - { name: "macOS Latest Debug (Intel)", - os: macos-13, + os: macos-15-intel, + build_type: "Debug", cc: "clang", cxx: "clang++", + build_gen: "Unix Makefiles" + } + - { + name: "macOS Latest Release (Apple Silicon)", + os: macos-14, + build_type: "Release", cc: "clang", cxx: "clang++", + build_gen: "Unix Makefiles" + } + - { + name: "macOS Latest Debug (Apple Silicon)", + os: macos-14, build_type: "Debug", cc: "clang", cxx: "clang++", build_gen: "Unix Makefiles" } -# - { -# name: "macOS Latest Release (Apple Silicon)", -# os: macos-14-arm64, -# build_type: "Release", cc: "clang", cxx: "clang++", -# build_gen: "Unix Makefiles" -# } -# - { -# name: "macOS Latest Debug (Apple Silicon)", -# os: macos-14-arm64, -# build_type: "Debug", cc: "clang", cxx: "clang++", -# build_gen: "Unix Makefiles" -# } - { name: "Windows Latest MSVC Debug", os: windows-latest, @@ -79,7 +108,7 @@ jobs: } steps: - name: Checkout doxygen - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -92,6 +121,12 @@ jobs: - name: Install LaTeX (Linux) run: | + # workaround to avoid hanging on restarting the snapd after installing the firefox snap + sudo snap remove --purge firefox + sudo snap remove --purge snapd + sudo apt remove -y --purge snapd + sudo apt-mark hold snapd + # end workaround sudo apt update --fix-missing sudo apt upgrade sudo apt update @@ -100,53 +135,58 @@ jobs: - name: Install LaTeX (MacOS) run: | - brew update - brew install --cask mactex; + brew update || true + brew install --cask mactex || true echo "/Library/TeX/texbin/" >> $GITHUB_PATH - if: matrix.config.os == 'macos-13' + if: startsWith(matrix.config.os,'macos-') - - name: Install libclang (Ubuntu 20.04) + - name: Install libclang (Ubuntu 24.04) run: | - sudo apt update - sudo apt remove llvm-8 clang-8 libclang-common-8-dev clang-format-8 libllvm8 - sudo apt remove llvm-9 llvm-9-dev llvm-9-tools llvm-9-runtime clang-9 libclang-common-9-dev clang-format-9 libllvm9 - #sudo apt remove llvm-10 llvm-10-dev llvm-10-tools llvm-10-runtime clang-10 clang-format-10 libclang-common-10-dev libclang-cpp10 libclang1-10 libllvm10 - sudo apt remove llvm-11 llvm-11-dev llvm-11-tools llvm-11-runtime clang-11 clang-format-11 libclang-common-11-dev libclang-cpp11 libclang1-11 libllvm11 - sudo apt remove llvm-12 llvm-12-dev llvm-12-tools llvm-12-runtime clang-12 clang-format-12 libclang-common-12-dev libclang-cpp12 libclang1-12 libllvm12 + sudo apt remove llvm-14 llvm-14-dev llvm-14-tools llvm-14-runtime clang-14 clang-format-14 libclang-common-14-dev libclang-cpp14 libclang1-14 libllvm14 + sudo apt remove llvm-15 llvm-15-dev llvm-15-tools llvm-15-runtime clang-15 clang-format-15 libclang-common-15-dev libclang-cpp15 libclang1-15 libllvm15 + sudo apt remove llvm-16 llvm-16-dev llvm-16-tools llvm-16-runtime clang-16 clang-format-16 libclang-common-16-dev libclang-cpp16 libclang1-16 libllvm16 + sudo apt remove llvm-17 llvm-17-dev llvm-17-tools llvm-17-runtime clang-17 clang-format-17 libclang-common-17-dev libclang-cpp17 libclang1-17 libllvm17 sudo apt-get autoremove sudo apt-get clean - sudo apt install libclang-common-10-dev libclang-10-dev + sudo apt install -y libclang-common-18-dev libclang-18-dev clang-18 llvm-18 llvm-18-dev apt list --installed | egrep '(clang|llvm)' ls -d /usr/lib/llvm-*/include/ - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-10 100 - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-10 100 + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100 ls -al /usr/bin/clang++ ls -al /etc/alternatives/clang++ which clang++ clang++ -v - if: matrix.config.os == 'ubuntu-20.04' + if: matrix.config.cc == 'clang' && startsWith(matrix.config.os,'ubuntu-24') - - name: Install libxapian (Ubuntu 20.04) - run: | - sudo apt update - sudo apt install libxapian-dev - if: matrix.config.os == 'ubuntu-20.04' + - name: Install libxapian (Ubuntu 24.04) + run: sudo apt install libxapian-dev + if: startsWith(matrix.config.os,'ubuntu-24') - - name: Install LaTeX (Windows) - uses: teatimeguest/setup-texlive-action@v3 + - name: Install MikTeX (Windows) + uses: nick-invision/retry@v3 with: - packages: >- - scheme-medium - collection-latexextra - babel-dutch - cjk - bibtex + timeout_minutes: 15 + max_attempts: 5 + retry_wait_seconds: 60 + command: | + choco install miktex --debug --verbose --no-progress --params "/Set:basic" -y + if ($?) { echo "C:\Program Files\MiKTeX\miktex\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 } if: matrix.config.os == 'windows-latest' - - name: Install Ghostscript (Linux) + - name: Configure MikTeX (Windows) run: | - sudo apt update - sudo apt-get install ghostscript + miktex --admin --verbose packages update-package-database + miktex --admin --verbose packages update + miktex --admin --verbose fndb refresh + initexmf --admin --verbose --set-config-value=[MPM]AutoInstall=1 + initexmf --admin --verbose --update-fndb + initexmf --admin --verbose --mklinks --force + updmap --admin + if: matrix.config.os == 'windows-latest' + + - name: Install Ghostscript (Linux) + run: sudo apt-get install ghostscript if: startsWith(matrix.config.os,'ubuntu-') - name: Install Ghostscript (Windows) @@ -164,18 +204,15 @@ jobs: if: matrix.config.os == 'windows-latest' - name: Install xmllint (Linux) - run: | - sudo apt-get update - sudo apt-get install libxml2-utils + run: sudo apt-get install libxml2-utils if: startsWith(matrix.config.os,'ubuntu-') - name: Install xmllint (MacOS) run: | - brew install --force --overwrite python@3.11 # temp hack - brew install --force --overwrite python@3.12 # temp hack - brew update + brew update || true + brew upgrade || true brew install libxml2 - if: matrix.config.os == 'macos-13' + if: startsWith(matrix.config.os,'macos-') - name: Install bison (MacOS) run: | @@ -183,18 +220,16 @@ jobs: brew install bison; echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH #echo "/usr/local/opt/bison/bin" >> $GITHUB_PATH - if: matrix.config.os == 'macos-13' + if: startsWith(matrix.config.os,'macos-') - name: Install bison/flex (Windows) run: | #Choco-Install -PackageName winflexbison - choco install winflexbison + choco install winflexbison3 if: matrix.config.os == 'windows-latest' - name: Install Graphviz (Linux) - run: | - sudo apt update - sudo apt-get install graphviz + run: sudo apt-get install graphviz if: startsWith(matrix.config.os,'ubuntu-') - name: Install Graphviz (MacOS) @@ -207,7 +242,7 @@ jobs: # Try again brew install graphviz fi - if: matrix.config.os == 'macos-13' + if: startsWith(matrix.config.os,'macos-') - name: Install Graphviz (Windows) run: @@ -224,17 +259,23 @@ jobs: refreshenv if: matrix.config.os == 'windows-latest' - - name: Install Qt 6 - uses: jurplel/install-qt-action@v4 + - name: Install Qt 6.8 + uses: jdpurcell/install-qt-action@v5 + with: + version: 6.8.* + if: matrix.config.os == 'ubuntu-24.04-arm' + + - name: Install Qt 6.2 + uses: jdpurcell/install-qt-action@v5 with: version: 6.2.* - if: matrix.config.is == 'macos-13' + if: startsWith(matrix.config.os,'macos-') - name: Install Qt 5 - uses: jurplel/install-qt-action@v4 + uses: jdpurcell/install-qt-action@v5 with: version: 5.* - if: matrix.config.is != 'macos-13' + if: startsWith(matrix.config.os,'macos-')!=true && matrix.config.os != 'ubuntu-24.04-arm' - name: Check tool versions (Linux / MacOS) shell: bash @@ -327,7 +368,7 @@ jobs: endif() - name: Archive build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: "${{ matrix.config.name }} build artifacts" path: build/bin/ @@ -339,7 +380,7 @@ jobs: execute_process( COMMAND - cmake -E env TEST_FLAGS="--xml --xmlxsd --xhtml --qhp --docbook --rtf" + cmake -E env TEST_FLAGS="--xml --xmlxsd --xhtml --qhp --docbook --rtf --man" cmake --build build --target tests RESULT_VARIABLE result ) @@ -355,7 +396,7 @@ jobs: execute_process( COMMAND - cmake -E env TEST_FLAGS="--xml --xmlxsd --xhtml --qhp --docbook --rtf --pdf" + cmake -E env TEST_FLAGS="--xml --xmlxsd --xhtml --qhp --docbook --rtf --man --pdf" cmake --build build --target tests RESULT_VARIABLE result ) @@ -376,19 +417,18 @@ jobs: endif() - name: Archive html documentation artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: "Html documentation artifacts" path: build/html/ - if: matrix.config.name == 'Ubuntu Latest GCC Release' - + if: matrix.config.name == 'Ubuntu Latest GCC Release (Intel)' - name: Archive Latex documentation artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: "Latex documentation artifacts" path: build/latex/doxygen_manual.pdf - if: matrix.config.name == 'Ubuntu Latest GCC Release' + if: matrix.config.name == 'Ubuntu Latest GCC Release (Intel)' - name: Generate Internal documentation shell: cmake -P {0} @@ -400,7 +440,7 @@ jobs: if (NOT result EQUAL 0) message(FATAL_ERROR "Building internal documentation failed") endif() - if: matrix.config.name == 'Ubuntu Latest GCC Release' + if: matrix.config.name == 'Ubuntu Latest GCC Release (Intel)' - name: Publish Internal documentation to Github pages uses: peaceiris/actions-gh-pages@v4 @@ -409,5 +449,6 @@ jobs: external_repository: doxygen/doxygen-docs publish_dir: build/doxygen_docs/html force_orphan: true - if: ${{ github.event_name == 'push' && matrix.config.name == 'Ubuntu Latest GCC Release' }} + if: ${{ github.event_name == 'push' && matrix.config.name == 'Ubuntu Latest GCC Release (Intel)' }} + diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index b85602d3c01..1646711e604 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -14,12 +14,12 @@ permissions: jobs: check_date: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Check latest commit outputs: should_run: ${{ steps.should_run.outputs.should_run }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: print latest_commit run: echo ${{ github.sha }} @@ -32,7 +32,7 @@ jobs: scan: needs: check_date if: ${{ needs.check_date.outputs.should_run != 'false' }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: CC: gcc @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout doxygen - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Download Coverity run: | diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 4d3860906a1..dbffee2176f 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -17,10 +17,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Log in to the Container registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -28,12 +28,12 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image - uses: docker/build-push-action@a254f8ca60a858f3136a2f1f23a60969f2c402dd + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 with: context: . push: true diff --git a/.github/workflows/doxmlparser-publish.yml b/.github/workflows/doxmlparser-publish.yml index 34cccbc2e7e..d2a4fb5cb31 100644 --- a/.github/workflows/doxmlparser-publish.yml +++ b/.github/workflows/doxmlparser-publish.yml @@ -12,9 +12,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/.github/workflows/texlive.packages b/.github/workflows/texlive.packages new file mode 100644 index 00000000000..63e96416936 --- /dev/null +++ b/.github/workflows/texlive.packages @@ -0,0 +1,4 @@ +collection-latexextra +babel-dutch +cjk +bibtex diff --git a/.github/workflows/texlive.profile b/.github/workflows/texlive.profile new file mode 100644 index 00000000000..21d2105eb3d --- /dev/null +++ b/.github/workflows/texlive.profile @@ -0,0 +1,41 @@ +# Reference: https://www.tug.org/texlive/doc/install-tl.html#PROFILES + +#=[ Schemes ]=================================================================== +# You can pick your scheme. Availability depends upon the package repository, +# but common ones are: infraonly, minimal, basic, small, medium and full. +selected_scheme scheme-medium + +#=[ Collections ]=============================================================== +# For finer control, select `scheme-custom` above and pick your collections. +# You can list them, one per line, as follows: +# collection- 1 + +#=[ Paths ]===================================================================== +# These paths are the defaults in portable mode, which the action enforces. +# They are here for reference only and you should not modify them. +# TEXDIR $TEXLIVE_INSTALL_PREFIX +# TEXMFLOCAL $TEXLIVE_INSTALL_PREFIX/texmf-local +# TEXMFSYSVAR $TEXLIVE_INSTALL_PREFIX/texmf-var +# TEXMFSYSCONFIG $TEXLIVE_INSTALL_PREFIX/texmf-config +# TEXMFVAR $TEXMFSYSVAR +# TEXMFCONFIG $TEXMFSYSCONFIG +# TEXMFHOME $TEXMFLOCAL + +#=[ Installer options ]========================================================= +# Installer options can be provided but most have little use here. +# Note for power users: `instopt_portable` is enforced by the action +# to allow caching, so setting it here has no effect. + +#=[ TeX Live Package DataBase options ]========================================= +# Only three TLPDB options are relevant. Also, you should always set them for +# a responsible usage of storage space. +# Avoid installing package documentation files: +tlpdbopt_install_docfiles 0 +# Avoid installing package source files: +tlpdbopt_install_srcfiles 0 +# Avoid keeping backups: +tlpdbopt_autobackup 0 + +#=[ Platform options ]========================================================== +# You should not set the platform unless you know what you're doing, and let +# tlmgr and the action autodetect it. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ee195cc768..6468e292dbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,189 +22,248 @@ option(build_parse "Parses source code and dumps the dependencies between th option(build_search "Build external search tools (doxysearch and doxyindexer)" OFF) option(build_doc "Build user manual (HTML and PDF)" OFF) option(build_doc_chm "Build user manual (CHM)" OFF) -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - option(use_libc++ "Use libc++ as C++ standard library." ON) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + option(use_libc++ "Use libc++ as C++ standard library." ON) endif() option(use_libclang "Add support for libclang parsing." OFF) option(use_sys_spdlog "Use system spdlog library instead of the one bundled." OFF) +option(use_sys_fmt "Use system fmt library instead of the one bundled." OFF) option(use_sys_sqlite3 "Use system sqlite3 library instead of the one bundled." OFF) option(static_libclang "Link to a statically compiled version of LLVM/libclang." OFF) +option(static_libxapian "Link to a statically compiled version of libxapian." OFF) option(win_static "Link with /MT in stead of /MD on windows" OFF) option(enable_console "Enable that executables on Windows get the CONSOLE bit set for the doxywizard executable [development]" OFF) option(enable_coverage "Enable coverage reporting for gcc/clang [development]" OFF) option(enable_tracing "Enable tracing option in release builds [development]" OFF) option(enable_lex_debug "Enable debugging info for lexical scanners in release builds [development]" OFF) +if(CMAKE_BUILD_TYPE STREQUAL "Release") + if (CYGWIN OR MINGW OR NOT "${CMAKE_TOOLCHAIN_FILE}" STREQUAL "") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) + else() + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() +endif() + +include(CheckCXXCompilerFlag) set(force_qt CACHE INTERNAL "Forces doxywizard to build using the specified major version, this can be Qt5 or Qt6") set_property(CACHE force_qt PROPERTY STRINGS OFF Qt6 Qt5) -SET(enlarge_lex_buffers "262144" CACHE INTERNAL "Sets the lex input and read buffers to the specified size") +set(enlarge_lex_buffers "262144" CACHE INTERNAL "Sets the lex input and read buffers to the specified size") if(enable_coverage) - if ("${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") + if("${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") message(FATAL_ERROR "Doxygen cannot be generated in-place, the build directory (${PROJECT_BINARY_DIR}) has to differ from the doxygen main directory (${PROJECT_SOURCE_DIR})\nPlease don't forget to remove the already created file 'CMakeCache.txt' and the directory 'CMakeFiles'!") endif() endif() list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Sanitizers") -set(TOP "${PROJECT_SOURCE_DIR}") include(version) message(STATUS "Using Cmake version ${CMAKE_VERSION}") -if (${CMAKE_VERSION} VERSION_LESS "3.21.0") - set(depfile_supported "0" CACHE INTERNAL "DEPFILE is not supported") +if(${CMAKE_VERSION} VERSION_LESS "3.21.0") + set(depfile_supported "0" CACHE INTERNAL "DEPFILE is not supported") else() - set(depfile_supported "1" CACHE INTERNAL "DEPFILE is supported") + set(depfile_supported "1" CACHE INTERNAL "DEPFILE is supported") endif() set(clang "0" CACHE INTERNAL "used in settings.h") -set(MACOS_VERSION_MIN 10.14) -if (use_libclang) - set(clang "1" CACHE INTERNAL "used in settings.h") - find_package(LLVM CONFIG REQUIRED) - find_package(Clang CONFIG REQUIRED) +if(use_libclang) + set(clang "1" CACHE INTERNAL "used in settings.h") + find_package(LLVM CONFIG REQUIRED) + find_package(Clang CONFIG REQUIRED) endif() -if (use_sys_spdlog) - find_package(spdlog CONFIG REQUIRED) +if(use_sys_fmt) + find_package(fmt CONFIG REQUIRED) endif() -if (use_sys_sqlite3) - find_package(SQLite3 REQUIRED) +if(use_sys_spdlog) + find_package(spdlog CONFIG REQUIRED) endif() -if (build_wizard) - if (force_qt STREQUAL "Qt6") - if (CMAKE_SYSTEM MATCHES "Darwin") - set(MACOS_VERSION_MIN 10.15) - endif() - endif() +if(use_sys_sqlite3) + find_package(SQLite3 REQUIRED) +endif() + +if(NOT DEFINED MACOS_VERSION_MIN) # to allow overruling with -DMACOS_VERSION_MIN + set(MACOS_VERSION_MIN 10.14) + if(build_search AND CMAKE_SYSTEM MATCHES "Darwin") + set(MACOS_VERSION_MIN 10.15) # needs std::filesystem + endif() + if(build_wizard AND CMAKE_SYSTEM MATCHES "Darwin") + set(MACOS_VERSION_MIN 13.0) # needs Qt6 + endif() endif() -# use C++17 standard for compiling -set(CMAKE_CXX_STANDARD 17) +# use C++17 standard for compiling (unless very new Clang is present) +if( + (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17) OR + (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) +) + set(CMAKE_CXX_STANDARD 20) +else() + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) -if (ENABLE_CLANG_TIDY) - find_program("CLANGTIDY" "clang-tidy") - if (CLANGTIDY) - set(CMAKE_CXX_CLANG_TIDY clang-tidy; - -header-filter=.; - -checks=-*,cppcoreguidelines-special-member-functions - #-checks=-*,cppcoreguidelines-missing-std-forward - #-checks=-*,cppcoreguidelines-init-variables - #-checks=-*,cppcoreguidelines-misleading-capture-default-by-value - #-checks=-*,modernize-use-nullptr - #-checks=-*,modernize-use-override - #-checks=-*,modernize-use-emplace - ) - else() - message(SEND_ERROR "clang-tidy requested but executable not found") - endif() +if(ENABLE_CLANG_TIDY) + find_program(CLANG_TIDY clang-tidy) + if(CLANG_TIDY) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY}) + else() + message(SEND_ERROR "clang-tidy requested but executable not found") + endif() endif() # produce compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if (CMAKE_SYSTEM MATCHES "Darwin") - set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION_MIN}" CACHE STRING "Minimum OS X deployment version" FORCE) - set(CMAKE_CXX_FLAGS "-Wno-deprecated-register -mmacosx-version-min=${MACOS_VERSION_MIN} ${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS "-Wno-deprecated-register -mmacosx-version-min=${MACOS_VERSION_MIN} ${CMAKE_C_FLAGS}") - find_library(CORESERVICES_LIB CoreServices) - set(EXTRA_LIBS ${CORESERVICES_LIB}) +if(CMAKE_SYSTEM MATCHES "Darwin") + set(CMAKE_OSX_DEPLOYMENT_TARGET "${MACOS_VERSION_MIN}" CACHE STRING "Minimum OS X deployment version") + set(CMAKE_CXX_FLAGS "-Wno-deprecated-register ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-Wno-deprecated-register ${CMAKE_C_FLAGS}") + find_library(CORESERVICES_LIB CoreServices) + set(EXTRA_LIBS ${CORESERVICES_LIB}) endif() -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_LIBCPP_ENABLE_ASSERTIONS=1") -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_ASSERTIONS") -endif() +check_cxx_source_compiles( + " + #include + + #if !defined(__clang__) || !defined(_LIBCPP_VERSION) + # error \"This is not clang with libcxx by llvm\" + #endif + + int main() { + return 0; + } + " + IS_CLANG_LIBCPP + FAIL_REGEX "This is not clang with libcxx by llvm" +) + +add_compile_definitions( + # LLVM's clang in combination with libc++ + $<$,$>:_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG> + # LLVM's clang or gcc in combination with libstdc++ (GNU) + $<$,$>>:_GLIBCXX_ASSERTIONS> +) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSQLITE_OMIT_LOAD_EXTENSION=1") -if (WIN32) - if (MSVC) - if (NOT ICONV_DIR) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/include" "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/x64") - elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) - list(APPEND CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/include" "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/x86") - endif() - else() - list(APPEND CMAKE_PREFIX_PATH ${ICONV_DIR}) - endif() - set(CMAKE_REQUIRED_DEFINITIONS "-DLIBICONV_STATIC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") # needed for language.cpp on 64bit - add_definitions(-DLIBICONV_STATIC -D_CRT_SECURE_NO_WARNINGS) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") - endif() - if (CMAKE_GENERATOR MATCHES "NMake Makefiles") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") +# Use 64-bit off_t on 32-bit Linux +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SIZEOF_VOID_P EQUAL 4) + # ensure 64bit offsets are used for filesystem accesses for 32bit compilation + add_compile_definitions(_FILE_OFFSET_BITS=64) +endif() + +if(WIN32) + if(MSVC) + if(NOT ICONV_DIR) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/include" "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/x64") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + list(APPEND CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/include" "${PROJECT_SOURCE_DIR}/deps/iconv_winbuild/x86") + endif() + else() + list(APPEND CMAKE_PREFIX_PATH ${ICONV_DIR}) endif() + set(CMAKE_REQUIRED_DEFINITIONS "-DLIBICONV_STATIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") # needed for language.cpp on 64bit + add_definitions(-DLIBICONV_STATIC -D_CRT_SECURE_NO_WARNINGS) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") + endif() + if(CMAKE_GENERATOR MATCHES "NMake Makefiles") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + endif() endif() -if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") +if(CYGWIN OR MINGW) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -Wa,-mbig-obj") - if (CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") - endif() + if(CMAKE_BUILD_TYPE STREQUAL "") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wa,-mbig-obj") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wa,-mbig-obj") endif() -if (WIN32 AND MSVC) - # workaround for GitHub runner, see https://github.com/actions/runner-images/issues/10004 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") +if(WIN32 AND MSVC) + # workaround for GitHub runner, see https://github.com/actions/runner-images/issues/10004 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") endif() -if (CMAKE_SYSTEM_NAME MATCHES "Windows") - if ((CMAKE_GENERATOR MATCHES "MinGW Makefiles") OR - (CMAKE_GENERATOR MATCHES "MSYS Makefiles") OR - (CMAKE_GENERATOR MATCHES "Unix Makefiles")) +if(CMAKE_SYSTEM_NAME MATCHES "Windows") + if((CMAKE_GENERATOR MATCHES "MinGW Makefiles") OR + (CMAKE_GENERATOR MATCHES "MSYS Makefiles") OR + (CMAKE_GENERATOR MATCHES "Unix Makefiles")) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") - if (CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") - endif() - endif() + if(CMAKE_BUILD_TYPE STREQUAL "") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1") + endif() + endif() endif() # needed for JavaCC -set(JAVA_CC_EXTRA_FLAGS "-DJAVACC_CHAR_TYPE=\"unsigned char\"") +if(CMAKE_CXX_STANDARD EQUAL 20) + set(JAVA_CC_EXTRA_FLAGS "-DJAVACC_CHAR_TYPE=\"char8_t\"") +else() + set(JAVA_CC_EXTRA_FLAGS "-DJAVACC_CHAR_TYPE=\"unsigned char\"") +endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${JAVA_CC_EXTRA_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${JAVA_CC_EXTRA_FLAGS}") if(POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() + +if(POLICY CMP0116) + cmake_policy(SET CMP0116 NEW) +endif() + +# when using mutrace comment the next 3 lines and uncomment the last 2 set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic") +#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -rdynamic") -if (CMAKE_GENERATOR MATCHES "Ninja") +if(CMAKE_GENERATOR MATCHES "Ninja") set(LEX_FLAGS ) set(YACC_FLAGS ) set(JAVACC_FLAGS ) -else () +else() set(LEX_FLAGS $(LEX_FLAGS)) set(YACC_FLAGS $(YACC_FLAGS)) set(JAVACC_FLAGS $(JAVACC_FLAGS)) -endif () +endif() find_program(DOT NAMES dot) find_package(Python REQUIRED) find_package(FLEX REQUIRED) -if (FLEX_VERSION VERSION_LESS 2.5.37) +if(FLEX_VERSION VERSION_LESS 2.5.37) message(SEND_ERROR "Doxygen requires at least flex version 2.5.37 (installed: ${FLEX_VERSION})") endif() +if(FLEX_VERSION VERSION_LESS 2.6.0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dregister=") + if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_ALLOW_KEYWORD_MACROS=") + endif() +endif() find_package(BISON REQUIRED) -if (BISON_VERSION VERSION_LESS 2.7) +if(BISON_VERSION VERSION_LESS 2.7) message(SEND_ERROR "Doxygen requires at least bison version 2.7 (installed: ${BISON_VERSION})") endif() find_package(Threads) find_package(Sanitizers) -if ((CMAKE_BUILD_TYPE STREQUAL "Debug") OR enable_lex_debug) +if((CMAKE_BUILD_TYPE STREQUAL "Debug") OR enable_lex_debug) set(LEX_FLAGS "${LEX_FLAGS} -d") endif() @@ -226,21 +285,21 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${EXECUTABLE_OUTPUT_PATH}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${EXECUTABLE_OUTPUT_PATH}) -if (win_static) - set(CompilerFlags - CMAKE_CXX_FLAGS - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS - CMAKE_C_FLAGS_DEBUG - CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL - CMAKE_C_FLAGS_RELWITHDEBINFO) - foreach(CompilerFlag ${CompilerFlags}) - string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") - endforeach() +if(win_static) + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() endif() include(cmake/CompilerWarnings.cmake) @@ -253,27 +312,27 @@ add_subdirectory(libxml) add_subdirectory(vhdlparser) add_subdirectory(src) -if (build_doc_chm) - if (WIN32) - find_package(HTMLHelp REQUIRED) - set(build_doc ON) - else () - message(WARNING "CHM documentation generation not supported for this platform, ignoring setting.") - set(build_doc_chm OFF) - endif () -endif () +if(build_doc_chm) + if(WIN32) + find_package(HTMLHelp REQUIRED) + set(build_doc ON) + else() + message(WARNING "CHM documentation generation not supported for this platform, ignoring setting.") + set(build_doc_chm OFF) + endif() +endif() # always parse doc directory to at least install man pages add_subdirectory(doc) -if (build_doc) - add_subdirectory(examples) -endif () +if(build_doc) + add_subdirectory(examples) +endif() add_subdirectory(doc_internal) find_package(generateDS) set(update_doxmlparser_dependency "") -if (GENERATEDS_FOUND) +if(GENERATEDS_FOUND) set(update_doxmlparser_dependency "update_doxmlparser_files") endif() add_subdirectory(addon) diff --git a/Dockerfile b/Dockerfile index 2da13285fd2..1b30b8aa0c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM ubuntu:focal AS builder +FROM ubuntu:jammy AS builder RUN apt-get update && apt-get install -y \ g++ \ - python \ + python3 \ cmake \ flex \ bison \ @@ -18,7 +18,7 @@ RUN mkdir build \ && make install -FROM ubuntu:focal +FROM ubuntu:jammy RUN apt-get update && apt-get install --no-install-recommends -y \ graphviz \ && rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index 9a5b55f32a3..e10c9b3a670 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ and to some extent D. Doxygen also supports the hardware description language VH Doxygen can help you in three ways: -1. It can generate an on-line documentation browser (in HTML) and/or an - off-line reference manual (in LaTeX) from a set of documented source files. +1. It can generate an online documentation browser (in HTML) and/or an + offline reference manual (in LaTeX) from a set of documented source files. There is also support for generating output in RTF (MS-Word), PostScript, hyperlinked PDF, compressed HTML, DocBook and Unix man pages. The documentation is extracted directly from the sources, which makes @@ -22,16 +22,16 @@ Doxygen can help you in three ways: the various elements by means of include dependency graphs, inheritance diagrams, and collaboration diagrams, which are all generated automatically. 3. You can also use doxygen for creating normal documentation (as I did for - the doxygen user manual and doxygen web-site). + the doxygen user manual and doxygen website). Download --------- -The latest binaries and source of Doxygen can be downloaded from: +The latest binaries and Doxygen source code can be downloaded from: * https://www.doxygen.nl/ Developers --------- -* Linux & Windows and MacOS Build Status: Github Actions Build Status +* Linux, Windows and MacOS Build Status: Github Actions Build Status * Coverity Scan Build Status: Coverity Scan Build Status diff --git a/VERSION b/VERSION index 0eed1a29efd..15b989e398f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.0 +1.16.0 diff --git a/addon/doxmlparser/doxmlparser/compound.py b/addon/doxmlparser/doxmlparser/compound.py index 151c2deacf2..d3dfd71d2aa 100755 --- a/addon/doxmlparser/doxmlparser/compound.py +++ b/addon/doxmlparser/doxmlparser/compound.py @@ -1378,7 +1378,7 @@ class compounddefType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, id=None, kind=None, language=None, prot=None, final=None, inline=None, sealed=None, abstract=None, compoundname=None, title=None, basecompoundref=None, derivedcompoundref=None, includes=None, includedby=None, incdepgraph=None, invincdepgraph=None, innermodule=None, innerdir=None, innerfile=None, innerclass=None, innerconcept=None, innernamespace=None, innerpage=None, innergroup=None, qualifier=None, templateparamlist=None, sectiondef=None, tableofcontents=None, requiresclause=None, initializer=None, briefdescription=None, detaileddescription=None, exports=None, inheritancegraph=None, collaborationgraph=None, programlisting=None, location=None, listofallmembers=None, gds_collector_=None, **kwargs_): + def __init__(self, id=None, kind=None, language=None, prot=None, final=None, inline=None, sealed=None, abstract=None, compoundname=None, title=None, basecompoundref=None, derivedcompoundref=None, includes=None, includedby=None, incdepgraph=None, invincdepgraph=None, innermodule=None, innerdir=None, innerfile=None, innerclass=None, innerconcept=None, innernamespace=None, innerpage=None, innergroup=None, qualifier=None, templateparamlist=None, sectiondef=None, tableofcontents=None, requiresclause=None, initializer=None, conceptparts=None, briefdescription=None, detaileddescription=None, exports=None, inheritancegraph=None, collaborationgraph=None, programlisting=None, location=None, listofallmembers=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -1486,6 +1486,8 @@ def __init__(self, id=None, kind=None, language=None, prot=None, final=None, inl self.requiresclause_nsprefix_ = None self.initializer = initializer self.initializer_nsprefix_ = None + self.conceptparts = conceptparts + self.conceptparts_nsprefix_ = None self.briefdescription = briefdescription self.briefdescription_nsprefix_ = None self.detaileddescription = detaileddescription @@ -1689,6 +1691,10 @@ def get_initializer(self): return self.initializer def set_initializer(self, initializer): self.initializer = initializer + def get_conceptparts(self): + return self.conceptparts + def set_conceptparts(self, conceptparts): + self.conceptparts = conceptparts def get_briefdescription(self): return self.briefdescription def set_briefdescription(self, briefdescription): @@ -1829,6 +1835,7 @@ def hasContent_(self): self.tableofcontents is not None or self.requiresclause is not None or self.initializer is not None or + self.conceptparts is not None or self.briefdescription is not None or self.detaileddescription is not None or self.exports is not None or @@ -1963,6 +1970,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', if self.initializer is not None: namespaceprefix_ = self.initializer_nsprefix_ + ':' if (UseCapturedNS_ and self.initializer_nsprefix_) else '' self.initializer.export(outfile, level, namespaceprefix_, namespacedef_='', name_='initializer', pretty_print=pretty_print) + if self.conceptparts is not None: + namespaceprefix_ = self.conceptparts_nsprefix_ + ':' if (UseCapturedNS_ and self.conceptparts_nsprefix_) else '' + self.conceptparts.export(outfile, level, namespaceprefix_, namespacedef_='', name_='conceptparts', pretty_print=pretty_print) if self.briefdescription is not None: namespaceprefix_ = self.briefdescription_nsprefix_ + ':' if (UseCapturedNS_ and self.briefdescription_nsprefix_) else '' self.briefdescription.export(outfile, level, namespaceprefix_, namespacedef_='', name_='briefdescription', pretty_print=pretty_print) @@ -2152,13 +2162,20 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec obj_.build(child_, gds_collector_=gds_collector_) self.initializer = obj_ obj_.original_tagname_ = 'initializer' + elif nodeName_ == 'conceptparts': + obj_ = conceptParts.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.conceptparts = obj_ + obj_.original_tagname_ = 'conceptparts' elif nodeName_ == 'briefdescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.briefdescription = obj_ obj_.original_tagname_ = 'briefdescription' elif nodeName_ == 'detaileddescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.detaileddescription = obj_ obj_.original_tagname_ = 'detaileddescription' @@ -2195,82 +2212,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec # end class compounddefType -class qualifier(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, qualifier) - if subclass is not None: - return subclass(*args_, **kwargs_) - if qualifier.subclass: - return qualifier.subclass(*args_, **kwargs_) - else: - return qualifier(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='qualifier', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('qualifier') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'qualifier': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='qualifier') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='qualifier', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='qualifier'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='qualifier', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class qualifier - - class listofallmembersType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None @@ -2563,158 +2504,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec # end class memberRefType -class scope(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, scope) - if subclass is not None: - return subclass(*args_, **kwargs_) - if scope.subclass: - return scope.subclass(*args_, **kwargs_) - else: - return scope(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='scope', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('scope') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'scope': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='scope') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='scope', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='scope'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='scope', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class scope - - -class name(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, name) - if subclass is not None: - return subclass(*args_, **kwargs_) - if name.subclass: - return name.subclass(*args_, **kwargs_) - else: - return name(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='name', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('name') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'name': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='name') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='name', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='name'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='name', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class name - - class docHtmlOnlyType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None @@ -3948,7 +3737,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.header = value_ self.header_nsprefix_ = child_.prefix elif nodeName_ == 'description': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.description = obj_ obj_.original_tagname_ = 'description' @@ -3969,7 +3759,7 @@ class memberdefType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, kind=None, id=None, prot=None, static=None, extern=None, strong=None, const=None, explicit=None, inline=None, refqual=None, virt=None, volatile=None, mutable=None, noexcept=None, noexceptexpression=None, nodiscard=None, constexpr=None, consteval=None, constinit=None, readable=None, writable=None, initonly=None, settable=None, privatesettable=None, protectedsettable=None, gettable=None, privategettable=None, protectedgettable=None, final=None, sealed=None, new=None, add=None, remove=None, raise_=None, optional=None, required=None, accessor=None, attribute=None, property=None, readonly=None, bound=None, removable=None, constrained=None, transient=None, maybevoid=None, maybedefault=None, maybeambiguous=None, templateparamlist=None, type_=None, definition=None, argsstring=None, name=None, qualifiedname=None, read=None, write=None, bitfield=None, reimplements=None, reimplementedby=None, qualifier=None, param=None, enumvalue=None, requiresclause=None, initializer=None, exceptions=None, briefdescription=None, detaileddescription=None, inbodydescription=None, location=None, references=None, referencedby=None, gds_collector_=None, **kwargs_): + def __init__(self, kind=None, id=None, prot=None, static=None, extern=None, strong=None, const=None, explicit=None, inline=None, refqual=None, virt=None, volatile=None, mutable=None, thread_local=None, noexcept=None, noexceptexpression=None, nodiscard=None, constexpr=None, consteval=None, constinit=None, readable=None, writable=None, initonly=None, settable=None, privatesettable=None, protectedsettable=None, gettable=None, privategettable=None, protectedgettable=None, final=None, sealed=None, new=None, add=None, remove=None, raise_=None, optional=None, required=None, accessor=None, attribute=None, property=None, readonly=None, bound=None, removable=None, constrained=None, transient=None, maybevoid=None, maybedefault=None, maybeambiguous=None, templateparamlist=None, type_=None, definition=None, argsstring=None, name=None, qualifiedname=None, read=None, write=None, bitfield=None, reimplements=None, reimplementedby=None, qualifier=None, param=None, enumvalue=None, requiresclause=None, initializer=None, exceptions=None, briefdescription=None, detaileddescription=None, inbodydescription=None, location=None, references=None, referencedby=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -4001,6 +3791,8 @@ def __init__(self, kind=None, id=None, prot=None, static=None, extern=None, stro self.volatile_nsprefix_ = None self.mutable = _cast(None, mutable) self.mutable_nsprefix_ = None + self.thread_local = _cast(None, thread_local) + self.thread_local_nsprefix_ = None self.noexcept = _cast(None, noexcept) self.noexcept_nsprefix_ = None self.noexceptexpression = _cast(None, noexceptexpression) @@ -4337,6 +4129,10 @@ def get_mutable(self): return self.mutable def set_mutable(self, mutable): self.mutable = mutable + def get_thread_local(self): + return self.thread_local + def set_thread_local(self, thread_local): + self.thread_local = thread_local def get_noexcept(self): return self.noexcept def set_noexcept(self, noexcept): @@ -4643,6 +4439,9 @@ def exportAttributes(self, outfile, level, already_processed, namespaceprefix_=' if self.mutable is not None and 'mutable' not in already_processed: already_processed.add('mutable') outfile.write(' mutable=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.mutable), input_name='mutable')), )) + if self.thread_local is not None and 'thread_local' not in already_processed: + already_processed.add('thread_local') + outfile.write(' thread_local=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.thread_local), input_name='thread_local')), )) if self.noexcept is not None and 'noexcept' not in already_processed: already_processed.add('noexcept') outfile.write(' noexcept=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.noexcept), input_name='noexcept')), )) @@ -4903,6 +4702,11 @@ def buildAttributes(self, node, attrs, already_processed): already_processed.add('mutable') self.mutable = value self.validate_DoxBool(self.mutable) # validate type DoxBool + value = find_attr_value_('thread_local', node) + if value is not None and 'thread_local' not in already_processed: + already_processed.add('thread_local') + self.thread_local = value + self.validate_DoxBool(self.thread_local) # validate type DoxBool value = find_attr_value_('noexcept', node) if value is not None and 'noexcept' not in already_processed: already_processed.add('noexcept') @@ -5167,17 +4971,20 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.exceptions = obj_ obj_.original_tagname_ = 'exceptions' elif nodeName_ == 'briefdescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.briefdescription = obj_ obj_.original_tagname_ = 'briefdescription' elif nodeName_ == 'detaileddescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.detaileddescription = obj_ obj_.original_tagname_ = 'detaileddescription' elif nodeName_ == 'inbodydescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.inbodydescription = obj_ obj_.original_tagname_ = 'inbodydescription' @@ -5199,509 +5006,54 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec # end class memberdefType -class definition(GeneratedsSuper): +class descriptionType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, gds_collector_=None, **kwargs_): + def __init__(self, title=None, para=None, internal=None, sect1=None, valueOf_=None, mixedclass_=None, content_=None, extensiontype_=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.ns_prefix_ = None + self.title = title + self.title_nsprefix_ = None + if para is None: + self.para = [] + else: + self.para = para + self.para_nsprefix_ = None + if internal is None: + self.internal = [] + else: + self.internal = internal + self.internal_nsprefix_ = None + if sect1 is None: + self.sect1 = [] + else: + self.sect1 = sect1 + self.sect1_nsprefix_ = None + self.valueOf_ = valueOf_ + self.extensiontype_ = extensiontype_ + if mixedclass_ is None: + self.mixedclass_ = MixedContainer + else: + self.mixedclass_ = mixedclass_ + if content_ is None: + self.content_ = [] + else: + self.content_ = content_ + self.valueOf_ = valueOf_ def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, definition) + CurrentSubclassModule_, descriptionType) if subclass is not None: return subclass(*args_, **kwargs_) - if definition.subclass: - return definition.subclass(*args_, **kwargs_) + if descriptionType.subclass: + return descriptionType.subclass(*args_, **kwargs_) else: - return definition(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='definition', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('definition') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'definition': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='definition') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='definition', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='definition'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='definition', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class definition - - -class argsstring(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, argsstring) - if subclass is not None: - return subclass(*args_, **kwargs_) - if argsstring.subclass: - return argsstring.subclass(*args_, **kwargs_) - else: - return argsstring(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='argsstring', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('argsstring') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'argsstring': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='argsstring') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='argsstring', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='argsstring'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='argsstring', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class argsstring - - -class qualifiedname(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, qualifiedname) - if subclass is not None: - return subclass(*args_, **kwargs_) - if qualifiedname.subclass: - return qualifiedname.subclass(*args_, **kwargs_) - else: - return qualifiedname(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='qualifiedname', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('qualifiedname') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'qualifiedname': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='qualifiedname') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='qualifiedname', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='qualifiedname'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='qualifiedname', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class qualifiedname - - -class read(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, read) - if subclass is not None: - return subclass(*args_, **kwargs_) - if read.subclass: - return read.subclass(*args_, **kwargs_) - else: - return read(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='read', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('read') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'read': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='read') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='read', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='read'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='read', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class read - - -class write(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, write) - if subclass is not None: - return subclass(*args_, **kwargs_) - if write.subclass: - return write.subclass(*args_, **kwargs_) - else: - return write(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='write', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('write') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'write': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='write') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='write', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='write'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='write', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class write - - -class bitfield(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, bitfield) - if subclass is not None: - return subclass(*args_, **kwargs_) - if bitfield.subclass: - return bitfield.subclass(*args_, **kwargs_) - else: - return bitfield(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='bitfield', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('bitfield') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'bitfield': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='bitfield') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='bitfield', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='bitfield'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='bitfield', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class bitfield - - -class descriptionType(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, title=None, para=None, internal=None, sect1=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - self.title = title - self.title_nsprefix_ = None - if para is None: - self.para = [] - else: - self.para = para - self.para_nsprefix_ = None - if internal is None: - self.internal = [] - else: - self.internal = internal - self.internal_nsprefix_ = None - if sect1 is None: - self.sect1 = [] - else: - self.sect1 = sect1 - self.sect1_nsprefix_ = None - self.valueOf_ = valueOf_ - if mixedclass_ is None: - self.mixedclass_ = MixedContainer - else: - self.mixedclass_ = mixedclass_ - if content_ is None: - self.content_ = [] - else: - self.content_ = content_ - self.valueOf_ = valueOf_ - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, descriptionType) - if subclass is not None: - return subclass(*args_, **kwargs_) - if descriptionType.subclass: - return descriptionType.subclass(*args_, **kwargs_) - else: - return descriptionType(*args_, **kwargs_) + return descriptionType(*args_, **kwargs_) factory = staticmethod(factory) def get_ns_prefix_(self): return self.ns_prefix_ @@ -5743,6 +5095,8 @@ def replace_sect1_at(self, index, value): self.sect1[index] = value def get_valueOf_(self): return self.valueOf_ def set_valueOf_(self, valueOf_): self.valueOf_ = valueOf_ + def get_extensiontype_(self): return self.extensiontype_ + def set_extensiontype_(self, extensiontype_): self.extensiontype_ = extensiontype_ def hasContent_(self): if ( self.title is not None or @@ -5779,6 +5133,14 @@ def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='d else: outfile.write('/>%s' % (eol_, )) def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='descriptionType'): + if self.extensiontype_ is not None and 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + outfile.write(' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"') + if ":" not in self.extensiontype_: + imported_ns_type_prefix_ = GenerateDSNamespaceTypePrefixes_.get(self.extensiontype_, '') + outfile.write(' xsi:type="%s%s"' % (imported_ns_type_prefix_, self.extensiontype_)) + else: + outfile.write(' xsi:type="%s"' % self.extensiontype_) pass def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='descriptionType', fromsubclass_=False, pretty_print=True): if not fromsubclass_: @@ -5818,7 +5180,10 @@ def build(self, node, gds_collector_=None): self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) return self def buildAttributes(self, node, attrs, already_processed): - pass + value = find_attr_value_('xsi:type', node) + if value is not None and 'xsi:type' not in already_processed: + already_processed.add('xsi:type') + self.extensiontype_ = value def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): if nodeName_ == 'title' and child_.text is not None: valuestr_ = child_.text @@ -6031,12 +5396,14 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.initializer = obj_ obj_.original_tagname_ = 'initializer' elif nodeName_ == 'briefdescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.briefdescription = obj_ obj_.original_tagname_ = 'briefdescription' elif nodeName_ == 'detaileddescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.detaileddescription = obj_ obj_.original_tagname_ = 'detaileddescription' @@ -6145,151 +5512,105 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec # end class templateparamlistType -class paramType(GeneratedsSuper): +class conceptParts(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, attributes=None, type_=None, declname=None, defname=None, array=None, defval=None, typeconstraint=None, briefdescription=None, gds_collector_=None, **kwargs_): + def __init__(self, codepart=None, docpart=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.ns_prefix_ = None - self.attributes = attributes - self.attributes_nsprefix_ = None - self.type_ = type_ - self.type__nsprefix_ = None - self.declname = declname - self.declname_nsprefix_ = None - self.defname = defname - self.defname_nsprefix_ = None - self.array = array - self.array_nsprefix_ = None - self.defval = defval - self.defval_nsprefix_ = None - self.typeconstraint = typeconstraint - self.typeconstraint_nsprefix_ = None - self.briefdescription = briefdescription - self.briefdescription_nsprefix_ = None + if codepart is None: + self.codepart = [] + else: + self.codepart = codepart + self.codepart_nsprefix_ = None + if docpart is None: + self.docpart = [] + else: + self.docpart = docpart + self.docpart_nsprefix_ = None def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, paramType) + CurrentSubclassModule_, conceptParts) if subclass is not None: return subclass(*args_, **kwargs_) - if paramType.subclass: - return paramType.subclass(*args_, **kwargs_) + if conceptParts.subclass: + return conceptParts.subclass(*args_, **kwargs_) else: - return paramType(*args_, **kwargs_) + return conceptParts(*args_, **kwargs_) factory = staticmethod(factory) def get_ns_prefix_(self): return self.ns_prefix_ def set_ns_prefix_(self, ns_prefix): self.ns_prefix_ = ns_prefix - def get_attributes(self): - return self.attributes - def set_attributes(self, attributes): - self.attributes = attributes - def get_type(self): - return self.type_ - def set_type(self, type_): - self.type_ = type_ - def get_declname(self): - return self.declname - def set_declname(self, declname): - self.declname = declname - def get_defname(self): - return self.defname - def set_defname(self, defname): - self.defname = defname - def get_array(self): - return self.array - def set_array(self, array): - self.array = array - def get_defval(self): - return self.defval - def set_defval(self, defval): - self.defval = defval - def get_typeconstraint(self): - return self.typeconstraint - def set_typeconstraint(self, typeconstraint): - self.typeconstraint = typeconstraint - def get_briefdescription(self): - return self.briefdescription - def set_briefdescription(self, briefdescription): - self.briefdescription = briefdescription + def get_codepart(self): + return self.codepart + def set_codepart(self, codepart): + self.codepart = codepart + def add_codepart(self, value): + self.codepart.append(value) + def insert_codepart_at(self, index, value): + self.codepart.insert(index, value) + def replace_codepart_at(self, index, value): + self.codepart[index] = value + def get_docpart(self): + return self.docpart + def set_docpart(self, docpart): + self.docpart = docpart + def add_docpart(self, value): + self.docpart.append(value) + def insert_docpart_at(self, index, value): + self.docpart.insert(index, value) + def replace_docpart_at(self, index, value): + self.docpart[index] = value def hasContent_(self): if ( - self.attributes is not None or - self.type_ is not None or - self.declname is not None or - self.defname is not None or - self.array is not None or - self.defval is not None or - self.typeconstraint is not None or - self.briefdescription is not None + self.codepart or + self.docpart ): return True else: return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='paramType', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('paramType') + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptParts', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('conceptParts') if imported_ns_def_ is not None: namespacedef_ = imported_ns_def_ if pretty_print: eol_ = '\n' else: eol_ = '' - if self.original_tagname_ is not None and name_ == 'paramType': + if self.original_tagname_ is not None and name_ == 'conceptParts': name_ = self.original_tagname_ if UseCapturedNS_ and self.ns_prefix_: namespaceprefix_ = self.ns_prefix_ + ':' showIndent(outfile, level, pretty_print) outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='paramType') + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='conceptParts') if self.hasContent_(): outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='paramType', pretty_print=pretty_print) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='conceptParts', pretty_print=pretty_print) showIndent(outfile, level, pretty_print) outfile.write('%s' % (namespaceprefix_, name_, eol_)) else: outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='paramType'): + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='conceptParts'): pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='paramType', fromsubclass_=False, pretty_print=True): + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptParts', fromsubclass_=False, pretty_print=True): if pretty_print: eol_ = '\n' else: eol_ = '' - if self.attributes is not None: - namespaceprefix_ = self.attributes_nsprefix_ + ':' if (UseCapturedNS_ and self.attributes_nsprefix_) else '' - showIndent(outfile, level, pretty_print) - outfile.write('<%sattributes>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.attributes), input_name='attributes')), namespaceprefix_ , eol_)) - if self.type_ is not None: - namespaceprefix_ = self.type__nsprefix_ + ':' if (UseCapturedNS_ and self.type__nsprefix_) else '' - self.type_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='type', pretty_print=pretty_print) - if self.declname is not None: - namespaceprefix_ = self.declname_nsprefix_ + ':' if (UseCapturedNS_ and self.declname_nsprefix_) else '' - showIndent(outfile, level, pretty_print) - outfile.write('<%sdeclname>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.declname), input_name='declname')), namespaceprefix_ , eol_)) - if self.defname is not None: - namespaceprefix_ = self.defname_nsprefix_ + ':' if (UseCapturedNS_ and self.defname_nsprefix_) else '' - showIndent(outfile, level, pretty_print) - outfile.write('<%sdefname>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.defname), input_name='defname')), namespaceprefix_ , eol_)) - if self.array is not None: - namespaceprefix_ = self.array_nsprefix_ + ':' if (UseCapturedNS_ and self.array_nsprefix_) else '' - showIndent(outfile, level, pretty_print) - outfile.write('<%sarray>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.array), input_name='array')), namespaceprefix_ , eol_)) - if self.defval is not None: - namespaceprefix_ = self.defval_nsprefix_ + ':' if (UseCapturedNS_ and self.defval_nsprefix_) else '' - self.defval.export(outfile, level, namespaceprefix_, namespacedef_='', name_='defval', pretty_print=pretty_print) - if self.typeconstraint is not None: - namespaceprefix_ = self.typeconstraint_nsprefix_ + ':' if (UseCapturedNS_ and self.typeconstraint_nsprefix_) else '' - self.typeconstraint.export(outfile, level, namespaceprefix_, namespacedef_='', name_='typeconstraint', pretty_print=pretty_print) - if self.briefdescription is not None: - namespaceprefix_ = self.briefdescription_nsprefix_ + ':' if (UseCapturedNS_ and self.briefdescription_nsprefix_) else '' - self.briefdescription.export(outfile, level, namespaceprefix_, namespacedef_='', name_='briefdescription', pretty_print=pretty_print) + for codepart_ in self.codepart: + namespaceprefix_ = self.codepart_nsprefix_ + ':' if (UseCapturedNS_ and self.codepart_nsprefix_) else '' + codepart_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='codepart', pretty_print=pretty_print) + for docpart_ in self.docpart: + namespaceprefix_ = self.docpart_nsprefix_ + ':' if (UseCapturedNS_ and self.docpart_nsprefix_) else '' + docpart_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='docpart', pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -6304,187 +5625,98 @@ def build(self, node, gds_collector_=None): def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - if nodeName_ == 'attributes': - value_ = child_.text - value_ = self.gds_parse_string(value_, node, 'attributes') - value_ = self.gds_validate_string(value_, node, 'attributes') - self.attributes = value_ - self.attributes_nsprefix_ = child_.prefix - elif nodeName_ == 'type': - obj_ = linkedTextType.factory(parent_object_=self) - obj_.build(child_, gds_collector_=gds_collector_) - self.type_ = obj_ - obj_.original_tagname_ = 'type' - elif nodeName_ == 'declname': - value_ = child_.text - value_ = self.gds_parse_string(value_, node, 'declname') - value_ = self.gds_validate_string(value_, node, 'declname') - self.declname = value_ - self.declname_nsprefix_ = child_.prefix - elif nodeName_ == 'defname': - value_ = child_.text - value_ = self.gds_parse_string(value_, node, 'defname') - value_ = self.gds_validate_string(value_, node, 'defname') - self.defname = value_ - self.defname_nsprefix_ = child_.prefix - elif nodeName_ == 'array': - value_ = child_.text - value_ = self.gds_parse_string(value_, node, 'array') - value_ = self.gds_validate_string(value_, node, 'array') - self.array = value_ - self.array_nsprefix_ = child_.prefix - elif nodeName_ == 'defval': - obj_ = linkedTextType.factory(parent_object_=self) + if nodeName_ == 'codepart': + obj_ = conceptCodePart.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) - self.defval = obj_ - obj_.original_tagname_ = 'defval' - elif nodeName_ == 'typeconstraint': - obj_ = linkedTextType.factory(parent_object_=self) - obj_.build(child_, gds_collector_=gds_collector_) - self.typeconstraint = obj_ - obj_.original_tagname_ = 'typeconstraint' - elif nodeName_ == 'briefdescription': - obj_ = descriptionType.factory(parent_object_=self) + self.codepart.append(obj_) + obj_.original_tagname_ = 'codepart' + elif nodeName_ == 'docpart': + obj_ = conceptDocPart.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) - self.briefdescription = obj_ - obj_.original_tagname_ = 'briefdescription' -# end class paramType + self.docpart.append(obj_) + obj_.original_tagname_ = 'docpart' +# end class conceptParts -class attributes(GeneratedsSuper): +class conceptCodePart(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, gds_collector_=None, **kwargs_): + def __init__(self, line=None, programlisting=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.ns_prefix_ = None + self.line = _cast(int, line) + self.line_nsprefix_ = None + self.programlisting = programlisting + self.programlisting_nsprefix_ = None def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, attributes) + CurrentSubclassModule_, conceptCodePart) if subclass is not None: return subclass(*args_, **kwargs_) - if attributes.subclass: - return attributes.subclass(*args_, **kwargs_) + if conceptCodePart.subclass: + return conceptCodePart.subclass(*args_, **kwargs_) else: - return attributes(*args_, **kwargs_) + return conceptCodePart(*args_, **kwargs_) factory = staticmethod(factory) def get_ns_prefix_(self): return self.ns_prefix_ def set_ns_prefix_(self, ns_prefix): self.ns_prefix_ = ns_prefix + def get_programlisting(self): + return self.programlisting + def set_programlisting(self, programlisting): + self.programlisting = programlisting + def get_line(self): + return self.line + def set_line(self, line): + self.line = line def hasContent_(self): if ( - + self.programlisting is not None ): return True else: return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='attributes', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('attributes') + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptCodePart', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('conceptCodePart') if imported_ns_def_ is not None: namespacedef_ = imported_ns_def_ if pretty_print: eol_ = '\n' else: eol_ = '' - if self.original_tagname_ is not None and name_ == 'attributes': + if self.original_tagname_ is not None and name_ == 'conceptCodePart': name_ = self.original_tagname_ if UseCapturedNS_ and self.ns_prefix_: namespaceprefix_ = self.ns_prefix_ + ':' showIndent(outfile, level, pretty_print) outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='attributes') + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='conceptCodePart') if self.hasContent_(): outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='attributes', pretty_print=pretty_print) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='conceptCodePart', pretty_print=pretty_print) + showIndent(outfile, level, pretty_print) outfile.write('%s' % (namespaceprefix_, name_, eol_)) else: outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='attributes'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='attributes', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class attributes - - -class declname(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, declname) - if subclass is not None: - return subclass(*args_, **kwargs_) - if declname.subclass: - return declname.subclass(*args_, **kwargs_) - else: - return declname(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='declname', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('declname') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='conceptCodePart'): + if self.line is not None and 'line' not in already_processed: + already_processed.add('line') + outfile.write(' line="%s"' % self.gds_format_integer(self.line, input_name='line')) + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptCodePart', fromsubclass_=False, pretty_print=True): if pretty_print: eol_ = '\n' else: eol_ = '' - if self.original_tagname_ is not None and name_ == 'declname': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='declname') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='declname', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='declname'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='declname', fromsubclass_=False, pretty_print=True): - pass + if self.programlisting is not None: + namespaceprefix_ = self.programlisting_nsprefix_ + ':' if (UseCapturedNS_ and self.programlisting_nsprefix_) else '' + self.programlisting.export(outfile, level, namespaceprefix_, namespacedef_='', name_='programlisting', pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -6497,70 +5729,111 @@ def build(self, node, gds_collector_=None): self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) return self def buildAttributes(self, node, attrs, already_processed): - pass + value = find_attr_value_('line', node) + if value is not None and 'line' not in already_processed: + already_processed.add('line') + self.line = self.gds_parse_integer(value, node, 'line') def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class declname + if nodeName_ == 'programlisting': + obj_ = listingType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.programlisting = obj_ + obj_.original_tagname_ = 'programlisting' +# end class conceptCodePart -class defname(GeneratedsSuper): +class conceptDocPart(descriptionType): __hash__ = GeneratedsSuper.__hash__ subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): + superclass = descriptionType + def __init__(self, title=None, para=None, internal=None, sect1=None, line=None, col=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.ns_prefix_ = None + super(globals().get("conceptDocPart"), self).__init__(title, para, internal, sect1, valueOf_, mixedclass_, content_, **kwargs_) + self.line = _cast(int, line) + self.line_nsprefix_ = None + self.col = _cast(int, col) + self.col_nsprefix_ = None + self.valueOf_ = valueOf_ + if mixedclass_ is None: + self.mixedclass_ = MixedContainer + else: + self.mixedclass_ = mixedclass_ + if content_ is None: + self.content_ = [] + else: + self.content_ = content_ + self.valueOf_ = valueOf_ def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, defname) + CurrentSubclassModule_, conceptDocPart) if subclass is not None: return subclass(*args_, **kwargs_) - if defname.subclass: - return defname.subclass(*args_, **kwargs_) + if conceptDocPart.subclass: + return conceptDocPart.subclass(*args_, **kwargs_) else: - return defname(*args_, **kwargs_) + return conceptDocPart(*args_, **kwargs_) factory = staticmethod(factory) def get_ns_prefix_(self): return self.ns_prefix_ def set_ns_prefix_(self, ns_prefix): self.ns_prefix_ = ns_prefix + def get_line(self): + return self.line + def set_line(self, line): + self.line = line + def get_col(self): + return self.col + def set_col(self, col): + self.col = col + def get_valueOf_(self): return self.valueOf_ + def set_valueOf_(self, valueOf_): self.valueOf_ = valueOf_ def hasContent_(self): if ( - + (1 if type(self.valueOf_) in [int,float] else self.valueOf_) or + self.content_ or + super(conceptDocPart, self).hasContent_() ): return True else: return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='defname', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('defname') + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptDocPart', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('conceptDocPart') if imported_ns_def_ is not None: namespacedef_ = imported_ns_def_ if pretty_print: eol_ = '\n' else: eol_ = '' - if self.original_tagname_ is not None and name_ == 'defname': + if self.original_tagname_ is not None and name_ == 'conceptDocPart': name_ = self.original_tagname_ if UseCapturedNS_ and self.ns_prefix_: namespaceprefix_ = self.ns_prefix_ + ':' showIndent(outfile, level, pretty_print) outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='defname') + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='conceptDocPart') if self.hasContent_(): outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='defname', pretty_print=pretty_print) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='conceptDocPart', pretty_print=pretty_print) + showIndent(outfile, level, pretty_print) outfile.write('%s' % (namespaceprefix_, name_, eol_)) else: outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='defname'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='defname', fromsubclass_=False, pretty_print=True): - pass + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='conceptDocPart'): + super(conceptDocPart, self).exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='conceptDocPart') + if self.line is not None and 'line' not in already_processed: + already_processed.add('line') + outfile.write(' line="%s"' % self.gds_format_integer(self.line, input_name='line')) + if self.col is not None and 'col' not in already_processed: + already_processed.add('col') + outfile.write(' col="%s"' % self.gds_format_integer(self.col, input_name='col')) + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='conceptDocPart', fromsubclass_=False, pretty_print=True): + super(conceptDocPart, self).exportChildren(outfile, level, namespaceprefix_, namespacedef_, name_, True, pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -6568,75 +5841,180 @@ def build(self, node, gds_collector_=None): already_processed = set() self.ns_prefix_ = node.prefix self.buildAttributes(node, node.attrib, already_processed) + self.valueOf_ = get_all_text_(node) + if node.text is not None: + obj_ = self.mixedclass_(MixedContainer.CategoryText, + MixedContainer.TypeNone, '', node.text) + self.content_.append(obj_) for child in node: nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) return self def buildAttributes(self, node, attrs, already_processed): - pass + value = find_attr_value_('line', node) + if value is not None and 'line' not in already_processed: + already_processed.add('line') + self.line = self.gds_parse_integer(value, node, 'line') + value = find_attr_value_('col', node) + if value is not None and 'col' not in already_processed: + already_processed.add('col') + self.col = self.gds_parse_integer(value, node, 'col') + super(conceptDocPart, self).buildAttributes(node, attrs, already_processed) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): + if not fromsubclass_ and child_.tail is not None: + obj_ = self.mixedclass_(MixedContainer.CategoryText, + MixedContainer.TypeNone, '', child_.tail) + self.content_.append(obj_) + super(conceptDocPart, self).buildChildren(child_, node, nodeName_, True) pass -# end class defname +# end class conceptDocPart -class array(GeneratedsSuper): +class paramType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, gds_collector_=None, **kwargs_): + def __init__(self, attributes=None, type_=None, declname=None, defname=None, array=None, defval=None, typeconstraint=None, briefdescription=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.ns_prefix_ = None + self.attributes = attributes + self.attributes_nsprefix_ = None + self.type_ = type_ + self.type__nsprefix_ = None + self.declname = declname + self.declname_nsprefix_ = None + self.defname = defname + self.defname_nsprefix_ = None + self.array = array + self.array_nsprefix_ = None + self.defval = defval + self.defval_nsprefix_ = None + self.typeconstraint = typeconstraint + self.typeconstraint_nsprefix_ = None + self.briefdescription = briefdescription + self.briefdescription_nsprefix_ = None def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, array) + CurrentSubclassModule_, paramType) if subclass is not None: return subclass(*args_, **kwargs_) - if array.subclass: - return array.subclass(*args_, **kwargs_) + if paramType.subclass: + return paramType.subclass(*args_, **kwargs_) else: - return array(*args_, **kwargs_) + return paramType(*args_, **kwargs_) factory = staticmethod(factory) def get_ns_prefix_(self): return self.ns_prefix_ def set_ns_prefix_(self, ns_prefix): self.ns_prefix_ = ns_prefix + def get_attributes(self): + return self.attributes + def set_attributes(self, attributes): + self.attributes = attributes + def get_type(self): + return self.type_ + def set_type(self, type_): + self.type_ = type_ + def get_declname(self): + return self.declname + def set_declname(self, declname): + self.declname = declname + def get_defname(self): + return self.defname + def set_defname(self, defname): + self.defname = defname + def get_array(self): + return self.array + def set_array(self, array): + self.array = array + def get_defval(self): + return self.defval + def set_defval(self, defval): + self.defval = defval + def get_typeconstraint(self): + return self.typeconstraint + def set_typeconstraint(self, typeconstraint): + self.typeconstraint = typeconstraint + def get_briefdescription(self): + return self.briefdescription + def set_briefdescription(self, briefdescription): + self.briefdescription = briefdescription def hasContent_(self): if ( - + self.attributes is not None or + self.type_ is not None or + self.declname is not None or + self.defname is not None or + self.array is not None or + self.defval is not None or + self.typeconstraint is not None or + self.briefdescription is not None ): return True else: return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='array', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('array') + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='paramType', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('paramType') if imported_ns_def_ is not None: namespacedef_ = imported_ns_def_ if pretty_print: eol_ = '\n' else: eol_ = '' - if self.original_tagname_ is not None and name_ == 'array': + if self.original_tagname_ is not None and name_ == 'paramType': name_ = self.original_tagname_ if UseCapturedNS_ and self.ns_prefix_: namespaceprefix_ = self.ns_prefix_ + ':' showIndent(outfile, level, pretty_print) outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='array') + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='paramType') if self.hasContent_(): outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='array', pretty_print=pretty_print) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='paramType', pretty_print=pretty_print) + showIndent(outfile, level, pretty_print) outfile.write('%s' % (namespaceprefix_, name_, eol_)) else: outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='array'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='array', fromsubclass_=False, pretty_print=True): + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='paramType'): pass + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='paramType', fromsubclass_=False, pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.attributes is not None: + namespaceprefix_ = self.attributes_nsprefix_ + ':' if (UseCapturedNS_ and self.attributes_nsprefix_) else '' + showIndent(outfile, level, pretty_print) + outfile.write('<%sattributes>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.attributes), input_name='attributes')), namespaceprefix_ , eol_)) + if self.type_ is not None: + namespaceprefix_ = self.type__nsprefix_ + ':' if (UseCapturedNS_ and self.type__nsprefix_) else '' + self.type_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='type', pretty_print=pretty_print) + if self.declname is not None: + namespaceprefix_ = self.declname_nsprefix_ + ':' if (UseCapturedNS_ and self.declname_nsprefix_) else '' + showIndent(outfile, level, pretty_print) + outfile.write('<%sdeclname>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.declname), input_name='declname')), namespaceprefix_ , eol_)) + if self.defname is not None: + namespaceprefix_ = self.defname_nsprefix_ + ':' if (UseCapturedNS_ and self.defname_nsprefix_) else '' + showIndent(outfile, level, pretty_print) + outfile.write('<%sdefname>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.defname), input_name='defname')), namespaceprefix_ , eol_)) + if self.array is not None: + namespaceprefix_ = self.array_nsprefix_ + ':' if (UseCapturedNS_ and self.array_nsprefix_) else '' + showIndent(outfile, level, pretty_print) + outfile.write('<%sarray>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.array), input_name='array')), namespaceprefix_ , eol_)) + if self.defval is not None: + namespaceprefix_ = self.defval_nsprefix_ + ':' if (UseCapturedNS_ and self.defval_nsprefix_) else '' + self.defval.export(outfile, level, namespaceprefix_, namespacedef_='', name_='defval', pretty_print=pretty_print) + if self.typeconstraint is not None: + namespaceprefix_ = self.typeconstraint_nsprefix_ + ':' if (UseCapturedNS_ and self.typeconstraint_nsprefix_) else '' + self.typeconstraint.export(outfile, level, namespaceprefix_, namespacedef_='', name_='typeconstraint', pretty_print=pretty_print) + if self.briefdescription is not None: + namespaceprefix_ = self.briefdescription_nsprefix_ + ':' if (UseCapturedNS_ and self.briefdescription_nsprefix_) else '' + self.briefdescription.export(outfile, level, namespaceprefix_, namespacedef_='', name_='briefdescription', pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -6651,8 +6029,52 @@ def build(self, node, gds_collector_=None): def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class array + if nodeName_ == 'attributes': + value_ = child_.text + value_ = self.gds_parse_string(value_, node, 'attributes') + value_ = self.gds_validate_string(value_, node, 'attributes') + self.attributes = value_ + self.attributes_nsprefix_ = child_.prefix + elif nodeName_ == 'type': + obj_ = linkedTextType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.type_ = obj_ + obj_.original_tagname_ = 'type' + elif nodeName_ == 'declname': + value_ = child_.text + value_ = self.gds_parse_string(value_, node, 'declname') + value_ = self.gds_validate_string(value_, node, 'declname') + self.declname = value_ + self.declname_nsprefix_ = child_.prefix + elif nodeName_ == 'defname': + value_ = child_.text + value_ = self.gds_parse_string(value_, node, 'defname') + value_ = self.gds_validate_string(value_, node, 'defname') + self.defname = value_ + self.defname_nsprefix_ = child_.prefix + elif nodeName_ == 'array': + value_ = child_.text + value_ = self.gds_parse_string(value_, node, 'array') + value_ = self.gds_validate_string(value_, node, 'array') + self.array = value_ + self.array_nsprefix_ = child_.prefix + elif nodeName_ == 'defval': + obj_ = linkedTextType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.defval = obj_ + obj_.original_tagname_ = 'defval' + elif nodeName_ == 'typeconstraint': + obj_ = linkedTextType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.typeconstraint = obj_ + obj_.original_tagname_ = 'typeconstraint' + elif nodeName_ == 'briefdescription': + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.briefdescription = obj_ + obj_.original_tagname_ = 'briefdescription' +# end class paramType class linkedTextType(GeneratedsSuper): @@ -7013,102 +6435,26 @@ def build(self, node, gds_collector_=None): def buildAttributes(self, node, attrs, already_processed): value = find_attr_value_('id', node) if value is not None and 'id' not in already_processed: - already_processed.add('id') - self.id = value - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - if nodeName_ == 'label': - value_ = child_.text - value_ = self.gds_parse_string(value_, node, 'label') - value_ = self.gds_validate_string(value_, node, 'label') - self.label = value_ - self.label_nsprefix_ = child_.prefix - elif nodeName_ == 'link': - obj_ = linkType.factory(parent_object_=self) - obj_.build(child_, gds_collector_=gds_collector_) - self.link = obj_ - obj_.original_tagname_ = 'link' - elif nodeName_ == 'childnode': - obj_ = childnodeType.factory(parent_object_=self) - obj_.build(child_, gds_collector_=gds_collector_) - self.childnode.append(obj_) - obj_.original_tagname_ = 'childnode' -# end class nodeType - - -class label(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, label) - if subclass is not None: - return subclass(*args_, **kwargs_) - if label.subclass: - return label.subclass(*args_, **kwargs_) - else: - return label(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='label', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('label') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'label': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='label') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='label', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='label'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='label', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass + already_processed.add('id') + self.id = value def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class label + if nodeName_ == 'label': + value_ = child_.text + value_ = self.gds_parse_string(value_, node, 'label') + value_ = self.gds_validate_string(value_, node, 'label') + self.label = value_ + self.label_nsprefix_ = child_.prefix + elif nodeName_ == 'link': + obj_ = linkType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.link = obj_ + obj_.original_tagname_ = 'link' + elif nodeName_ == 'childnode': + obj_ = childnodeType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.childnode.append(obj_) + obj_.original_tagname_ = 'childnode' +# end class nodeType class childnodeType(GeneratedsSuper): @@ -7253,82 +6599,6 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec # end class childnodeType -class edgelabel(GeneratedsSuper): - __hash__ = GeneratedsSuper.__hash__ - subclass = None - superclass = None - def __init__(self, gds_collector_=None, **kwargs_): - self.gds_collector_ = gds_collector_ - self.gds_elementtree_node_ = None - self.original_tagname_ = None - self.parent_object_ = kwargs_.get('parent_object_') - self.ns_prefix_ = None - def factory(*args_, **kwargs_): - if CurrentSubclassModule_ is not None: - subclass = getSubclassFromModule_( - CurrentSubclassModule_, edgelabel) - if subclass is not None: - return subclass(*args_, **kwargs_) - if edgelabel.subclass: - return edgelabel.subclass(*args_, **kwargs_) - else: - return edgelabel(*args_, **kwargs_) - factory = staticmethod(factory) - def get_ns_prefix_(self): - return self.ns_prefix_ - def set_ns_prefix_(self, ns_prefix): - self.ns_prefix_ = ns_prefix - def hasContent_(self): - if ( - - ): - return True - else: - return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='edgelabel', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('edgelabel') - if imported_ns_def_ is not None: - namespacedef_ = imported_ns_def_ - if pretty_print: - eol_ = '\n' - else: - eol_ = '' - if self.original_tagname_ is not None and name_ == 'edgelabel': - name_ = self.original_tagname_ - if UseCapturedNS_ and self.ns_prefix_: - namespaceprefix_ = self.ns_prefix_ + ':' - showIndent(outfile, level, pretty_print) - outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) - already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='edgelabel') - if self.hasContent_(): - outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='edgelabel', pretty_print=pretty_print) - outfile.write('%s' % (namespaceprefix_, name_, eol_)) - else: - outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='edgelabel'): - pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='edgelabel', fromsubclass_=False, pretty_print=True): - pass - def build(self, node, gds_collector_=None): - self.gds_collector_ = gds_collector_ - if SaveElementTreeNode: - self.gds_elementtree_node_ = node - already_processed = set() - self.ns_prefix_ = node.prefix - self.buildAttributes(node, node.attrib, already_processed) - for child in node: - nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] - self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) - return self - def buildAttributes(self, node, attrs, already_processed): - pass - def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): - pass -# end class edgelabel - - class linkType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None @@ -10470,7 +9740,7 @@ class docInternalS5Type(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, para=None, sect5=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): + def __init__(self, para=None, sect6=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -10481,11 +9751,11 @@ def __init__(self, para=None, sect5=None, valueOf_=None, mixedclass_=None, conte else: self.para = para self.para_nsprefix_ = None - if sect5 is None: - self.sect5 = [] + if sect6 is None: + self.sect6 = [] else: - self.sect5 = sect5 - self.sect5_nsprefix_ = None + self.sect6 = sect6 + self.sect6_nsprefix_ = None self.valueOf_ = valueOf_ if mixedclass_ is None: self.mixedclass_ = MixedContainer @@ -10521,22 +9791,22 @@ def insert_para_at(self, index, value): self.para.insert(index, value) def replace_para_at(self, index, value): self.para[index] = value - def get_sect5(self): - return self.sect5 - def set_sect5(self, sect5): - self.sect5 = sect5 - def add_sect5(self, value): - self.sect5.append(value) - def insert_sect5_at(self, index, value): - self.sect5.insert(index, value) - def replace_sect5_at(self, index, value): - self.sect5[index] = value + def get_sect6(self): + return self.sect6 + def set_sect6(self, sect6): + self.sect6 = sect6 + def add_sect6(self, value): + self.sect6.append(value) + def insert_sect6_at(self, index, value): + self.sect6.insert(index, value) + def replace_sect6_at(self, index, value): + self.sect6[index] = value def get_valueOf_(self): return self.valueOf_ def set_valueOf_(self, valueOf_): self.valueOf_ = valueOf_ def hasContent_(self): if ( self.para or - self.sect5 or + self.sect6 or (1 if type(self.valueOf_) in [int,float] else self.valueOf_) or self.content_ ): @@ -10579,9 +9849,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', for para_ in self.para: namespaceprefix_ = self.para_nsprefix_ + ':' if (UseCapturedNS_ and self.para_nsprefix_) else '' para_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='para', pretty_print=pretty_print) - for sect5_ in self.sect5: - namespaceprefix_ = self.sect5_nsprefix_ + ':' if (UseCapturedNS_ and self.sect5_nsprefix_) else '' - sect5_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='sect5', pretty_print=pretty_print) + for sect6_ in self.sect6: + namespaceprefix_ = self.sect6_nsprefix_ + ':' if (UseCapturedNS_ and self.sect6_nsprefix_) else '' + sect6_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='sect6', pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -10611,16 +9881,16 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.add_para(obj_.value) elif hasattr(self, 'set_para'): self.set_para(obj_.value) - elif nodeName_ == 'sect5': + elif nodeName_ == 'sect6': obj_ = docSect6Type.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) obj_ = self.mixedclass_(MixedContainer.CategoryComplex, - MixedContainer.TypeNone, 'sect5', obj_) + MixedContainer.TypeNone, 'sect6', obj_) self.content_.append(obj_) - if hasattr(self, 'add_sect5'): - self.add_sect5(obj_.value) - elif hasattr(self, 'set_sect5'): - self.set_sect5(obj_.value) + if hasattr(self, 'add_sect6'): + self.add_sect6(obj_.value) + elif hasattr(self, 'set_sect6'): + self.set_sect6(obj_.value) if not fromsubclass_ and child_.tail is not None: obj_ = self.mixedclass_(MixedContainer.CategoryText, MixedContainer.TypeNone, '', child_.tail) @@ -12645,7 +11915,7 @@ class docParaType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, emphasis=None, computeroutput=None, subscript=None, superscript=None, center=None, small=None, cite=None, del_=None, ins=None, htmlonly=None, manonly=None, xmlonly=None, rtfonly=None, latexonly=None, docbookonly=None, image=None, dot=None, msc=None, plantuml=None, anchor=None, formula=None, ref=None, emoji=None, linebreak=None, hruler=None, preformatted=None, programlisting=None, verbatim=None, javadocliteral=None, javadoccode=None, indexentry=None, orderedlist=None, itemizedlist=None, simplesect=None, title=None, variablelist=None, table=None, heading=None, dotfile=None, mscfile=None, diafile=None, toclist=None, language=None, parameterlist=None, xrefsect=None, copydoc=None, details=None, blockquote=None, parblock=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): + def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, emphasis=None, computeroutput=None, subscript=None, superscript=None, center=None, small=None, cite=None, del_=None, ins=None, htmlonly=None, manonly=None, xmlonly=None, rtfonly=None, latexonly=None, docbookonly=None, image=None, dot=None, msc=None, plantuml=None, anchor=None, formula=None, ref=None, emoji=None, linebreak=None, hruler=None, preformatted=None, programlisting=None, verbatim=None, javadocliteral=None, javadoccode=None, indexentry=None, orderedlist=None, itemizedlist=None, simplesect=None, title=None, variablelist=None, table=None, heading=None, dotfile=None, mscfile=None, diafile=None, plantumlfile=None, toclist=None, language=None, parameterlist=None, xrefsect=None, copydoc=None, details=None, blockquote=None, parblock=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -12881,6 +12151,11 @@ def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, e else: self.diafile = diafile self.diafile_nsprefix_ = None + if plantumlfile is None: + self.plantumlfile = [] + else: + self.plantumlfile = plantumlfile + self.plantumlfile_nsprefix_ = None if toclist is None: self.toclist = [] else: @@ -13406,6 +12681,16 @@ def insert_diafile_at(self, index, value): self.diafile.insert(index, value) def replace_diafile_at(self, index, value): self.diafile[index] = value + def get_plantumlfile(self): + return self.plantumlfile + def set_plantumlfile(self, plantumlfile): + self.plantumlfile = plantumlfile + def add_plantumlfile(self, value): + self.plantumlfile.append(value) + def insert_plantumlfile_at(self, index, value): + self.plantumlfile.insert(index, value) + def replace_plantumlfile_at(self, index, value): + self.plantumlfile[index] = value def get_toclist(self): return self.toclist def set_toclist(self, toclist): @@ -13536,6 +12821,7 @@ def hasContent_(self): self.dotfile or self.mscfile or self.diafile or + self.plantumlfile or self.toclist or self.language or self.parameterlist or @@ -13729,6 +13015,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', for diafile_ in self.diafile: namespaceprefix_ = self.diafile_nsprefix_ + ':' if (UseCapturedNS_ and self.diafile_nsprefix_) else '' diafile_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='diafile', pretty_print=pretty_print) + for plantumlfile_ in self.plantumlfile: + namespaceprefix_ = self.plantumlfile_nsprefix_ + ':' if (UseCapturedNS_ and self.plantumlfile_nsprefix_) else '' + plantumlfile_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='plantumlfile', pretty_print=pretty_print) for toclist_ in self.toclist: namespaceprefix_ = self.toclist_nsprefix_ + ':' if (UseCapturedNS_ and self.toclist_nsprefix_) else '' toclist_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='toclist', pretty_print=pretty_print) @@ -14216,6 +13505,16 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.add_diafile(obj_.value) elif hasattr(self, 'set_diafile'): self.set_diafile(obj_.value) + elif nodeName_ == 'plantumlfile': + obj_ = docImageFileType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + obj_ = self.mixedclass_(MixedContainer.CategoryComplex, + MixedContainer.TypeNone, 'plantumlfile', obj_) + self.content_.append(obj_) + if hasattr(self, 'add_plantumlfile'): + self.add_plantumlfile(obj_.value) + elif hasattr(self, 'set_plantumlfile'): + self.set_plantumlfile(obj_.value) elif nodeName_ == 'toclist': obj_ = docTocListType.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) @@ -14307,7 +13606,7 @@ class docMarkupType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, emphasis=None, computeroutput=None, subscript=None, superscript=None, center=None, small=None, cite=None, del_=None, ins=None, htmlonly=None, manonly=None, xmlonly=None, rtfonly=None, latexonly=None, docbookonly=None, image=None, dot=None, msc=None, plantuml=None, anchor=None, formula=None, ref=None, emoji=None, linebreak=None, hruler=None, preformatted=None, programlisting=None, verbatim=None, javadocliteral=None, javadoccode=None, indexentry=None, orderedlist=None, itemizedlist=None, simplesect=None, title=None, variablelist=None, table=None, heading=None, dotfile=None, mscfile=None, diafile=None, toclist=None, language=None, parameterlist=None, xrefsect=None, copydoc=None, details=None, blockquote=None, parblock=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): + def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, emphasis=None, computeroutput=None, subscript=None, superscript=None, center=None, small=None, cite=None, del_=None, ins=None, htmlonly=None, manonly=None, xmlonly=None, rtfonly=None, latexonly=None, docbookonly=None, image=None, dot=None, msc=None, plantuml=None, anchor=None, formula=None, ref=None, emoji=None, linebreak=None, hruler=None, preformatted=None, programlisting=None, verbatim=None, javadocliteral=None, javadoccode=None, indexentry=None, orderedlist=None, itemizedlist=None, simplesect=None, title=None, variablelist=None, table=None, heading=None, dotfile=None, mscfile=None, diafile=None, plantumlfile=None, toclist=None, language=None, parameterlist=None, xrefsect=None, copydoc=None, details=None, blockquote=None, parblock=None, valueOf_=None, mixedclass_=None, content_=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -14543,6 +13842,11 @@ def __init__(self, ulink=None, bold=None, s=None, strike=None, underline=None, e else: self.diafile = diafile self.diafile_nsprefix_ = None + if plantumlfile is None: + self.plantumlfile = [] + else: + self.plantumlfile = plantumlfile + self.plantumlfile_nsprefix_ = None if toclist is None: self.toclist = [] else: @@ -15068,6 +14372,16 @@ def insert_diafile_at(self, index, value): self.diafile.insert(index, value) def replace_diafile_at(self, index, value): self.diafile[index] = value + def get_plantumlfile(self): + return self.plantumlfile + def set_plantumlfile(self, plantumlfile): + self.plantumlfile = plantumlfile + def add_plantumlfile(self, value): + self.plantumlfile.append(value) + def insert_plantumlfile_at(self, index, value): + self.plantumlfile.insert(index, value) + def replace_plantumlfile_at(self, index, value): + self.plantumlfile[index] = value def get_toclist(self): return self.toclist def set_toclist(self, toclist): @@ -15198,6 +14512,7 @@ def hasContent_(self): self.dotfile or self.mscfile or self.diafile or + self.plantumlfile or self.toclist or self.language or self.parameterlist or @@ -15391,6 +14706,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', for diafile_ in self.diafile: namespaceprefix_ = self.diafile_nsprefix_ + ':' if (UseCapturedNS_ and self.diafile_nsprefix_) else '' diafile_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='diafile', pretty_print=pretty_print) + for plantumlfile_ in self.plantumlfile: + namespaceprefix_ = self.plantumlfile_nsprefix_ + ':' if (UseCapturedNS_ and self.plantumlfile_nsprefix_) else '' + plantumlfile_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='plantumlfile', pretty_print=pretty_print) for toclist_ in self.toclist: namespaceprefix_ = self.toclist_nsprefix_ + ':' if (UseCapturedNS_ and self.toclist_nsprefix_) else '' toclist_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='toclist', pretty_print=pretty_print) @@ -15878,6 +15196,16 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.add_diafile(obj_.value) elif hasattr(self, 'set_diafile'): self.set_diafile(obj_.value) + elif nodeName_ == 'plantumlfile': + obj_ = docImageFileType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + obj_ = self.mixedclass_(MixedContainer.CategoryComplex, + MixedContainer.TypeNone, 'plantumlfile', obj_) + self.content_.append(obj_) + if hasattr(self, 'add_plantumlfile'): + self.add_plantumlfile(obj_.value) + elif hasattr(self, 'set_plantumlfile'): + self.set_plantumlfile(obj_.value) elif nodeName_ == 'toclist': obj_ = docTocListType.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) @@ -26765,7 +26093,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.parameternamelist.append(obj_) obj_.original_tagname_ = 'parameternamelist' elif nodeName_ == 'parameterdescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.parameterdescription = obj_ obj_.original_tagname_ = 'parameterdescription' @@ -27294,7 +26623,8 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec self.xreftitle.append(value_) self.xreftitle_nsprefix_ = child_.prefix elif nodeName_ == 'xrefdescription': - obj_ = descriptionType.factory(parent_object_=self) + class_obj_ = self.get_class_obj_(child_, descriptionType) + obj_ = class_obj_.factory(parent_object_=self) obj_.build(child_, gds_collector_=gds_collector_) self.xrefdescription = obj_ obj_.original_tagname_ = 'xrefdescription' @@ -27854,7 +27184,7 @@ class tableofcontentsType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, tocsect=None, gds_collector_=None, **kwargs_): + def __init__(self, tocsect=None, tableofcontents=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -27865,6 +27195,11 @@ def __init__(self, tocsect=None, gds_collector_=None, **kwargs_): else: self.tocsect = tocsect self.tocsect_nsprefix_ = None + if tableofcontents is None: + self.tableofcontents = [] + else: + self.tableofcontents = tableofcontents + self.tableofcontents_nsprefix_ = None def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( @@ -27890,9 +27225,20 @@ def insert_tocsect_at(self, index, value): self.tocsect.insert(index, value) def replace_tocsect_at(self, index, value): self.tocsect[index] = value + def get_tableofcontents(self): + return self.tableofcontents + def set_tableofcontents(self, tableofcontents): + self.tableofcontents = tableofcontents + def add_tableofcontents(self, value): + self.tableofcontents.append(value) + def insert_tableofcontents_at(self, index, value): + self.tableofcontents.insert(index, value) + def replace_tableofcontents_at(self, index, value): + self.tableofcontents[index] = value def hasContent_(self): if ( - self.tocsect + self.tocsect or + self.tableofcontents ): return True else: @@ -27930,6 +27276,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', for tocsect_ in self.tocsect: namespaceprefix_ = self.tocsect_nsprefix_ + ':' if (UseCapturedNS_ and self.tocsect_nsprefix_) else '' tocsect_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='tocsect', pretty_print=pretty_print) + for tableofcontents_ in self.tableofcontents: + namespaceprefix_ = self.tableofcontents_nsprefix_ + ':' if (UseCapturedNS_ and self.tableofcontents_nsprefix_) else '' + tableofcontents_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='tableofcontents', pretty_print=pretty_print) def build(self, node, gds_collector_=None): self.gds_collector_ = gds_collector_ if SaveElementTreeNode: @@ -27949,14 +27298,121 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec obj_.build(child_, gds_collector_=gds_collector_) self.tocsect.append(obj_) obj_.original_tagname_ = 'tocsect' + elif nodeName_ == 'tableofcontents': + obj_ = tableofcontentsType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.tableofcontents.append(obj_) + obj_.original_tagname_ = 'tableofcontents' # end class tableofcontentsType +class tableofcontentsNameType(GeneratedsSuper): + __hash__ = GeneratedsSuper.__hash__ + subclass = None + superclass = None + def __init__(self, para=None, gds_collector_=None, **kwargs_): + self.gds_collector_ = gds_collector_ + self.gds_elementtree_node_ = None + self.original_tagname_ = None + self.parent_object_ = kwargs_.get('parent_object_') + self.ns_prefix_ = None + if para is None: + self.para = [] + else: + self.para = para + self.para_nsprefix_ = None + def factory(*args_, **kwargs_): + if CurrentSubclassModule_ is not None: + subclass = getSubclassFromModule_( + CurrentSubclassModule_, tableofcontentsNameType) + if subclass is not None: + return subclass(*args_, **kwargs_) + if tableofcontentsNameType.subclass: + return tableofcontentsNameType.subclass(*args_, **kwargs_) + else: + return tableofcontentsNameType(*args_, **kwargs_) + factory = staticmethod(factory) + def get_ns_prefix_(self): + return self.ns_prefix_ + def set_ns_prefix_(self, ns_prefix): + self.ns_prefix_ = ns_prefix + def get_para(self): + return self.para + def set_para(self, para): + self.para = para + def add_para(self, value): + self.para.append(value) + def insert_para_at(self, index, value): + self.para.insert(index, value) + def replace_para_at(self, index, value): + self.para[index] = value + def hasContent_(self): + if ( + self.para + ): + return True + else: + return False + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='tableofcontentsNameType', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('tableofcontentsNameType') + if imported_ns_def_ is not None: + namespacedef_ = imported_ns_def_ + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.original_tagname_ is not None and name_ == 'tableofcontentsNameType': + name_ = self.original_tagname_ + if UseCapturedNS_ and self.ns_prefix_: + namespaceprefix_ = self.ns_prefix_ + ':' + showIndent(outfile, level, pretty_print) + outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) + already_processed = set() + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='tableofcontentsNameType') + if self.hasContent_(): + outfile.write('>%s' % (eol_, )) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='tableofcontentsNameType', pretty_print=pretty_print) + showIndent(outfile, level, pretty_print) + outfile.write('%s' % (namespaceprefix_, name_, eol_)) + else: + outfile.write('/>%s' % (eol_, )) + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='tableofcontentsNameType'): + pass + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='tableofcontentsNameType', fromsubclass_=False, pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + for para_ in self.para: + namespaceprefix_ = self.para_nsprefix_ + ':' if (UseCapturedNS_ and self.para_nsprefix_) else '' + para_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='para', pretty_print=pretty_print) + def build(self, node, gds_collector_=None): + self.gds_collector_ = gds_collector_ + if SaveElementTreeNode: + self.gds_elementtree_node_ = node + already_processed = set() + self.ns_prefix_ = node.prefix + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_, gds_collector_=gds_collector_) + return self + def buildAttributes(self, node, attrs, already_processed): + pass + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collector_=None): + if nodeName_ == 'para': + obj_ = docParaType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.para.append(obj_) + obj_.original_tagname_ = 'para' +# end class tableofcontentsNameType + + class tableofcontentsKindType(GeneratedsSuper): __hash__ = GeneratedsSuper.__hash__ subclass = None superclass = None - def __init__(self, name=None, reference=None, tableofcontents=None, gds_collector_=None, **kwargs_): + def __init__(self, name=None, docs=None, reference=None, tableofcontents=None, gds_collector_=None, **kwargs_): self.gds_collector_ = gds_collector_ self.gds_elementtree_node_ = None self.original_tagname_ = None @@ -27964,6 +27420,8 @@ def __init__(self, name=None, reference=None, tableofcontents=None, gds_collecto self.ns_prefix_ = None self.name = name self.name_nsprefix_ = None + self.docs = docs + self.docs_nsprefix_ = None self.reference = reference self.reference_nsprefix_ = None if tableofcontents is None: @@ -27990,6 +27448,10 @@ def get_name(self): return self.name def set_name(self, name): self.name = name + def get_docs(self): + return self.docs + def set_docs(self, docs): + self.docs = docs def get_reference(self): return self.reference def set_reference(self, reference): @@ -28007,6 +27469,7 @@ def replace_tableofcontents_at(self, index, value): def hasContent_(self): if ( self.name is not None or + self.docs is not None or self.reference is not None or self.tableofcontents ): @@ -28047,6 +27510,9 @@ def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', namespaceprefix_ = self.name_nsprefix_ + ':' if (UseCapturedNS_ and self.name_nsprefix_) else '' showIndent(outfile, level, pretty_print) outfile.write('<%sname>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.name), input_name='name')), namespaceprefix_ , eol_)) + if self.docs is not None: + namespaceprefix_ = self.docs_nsprefix_ + ':' if (UseCapturedNS_ and self.docs_nsprefix_) else '' + self.docs.export(outfile, level, namespaceprefix_, namespacedef_='', name_='docs', pretty_print=pretty_print) if self.reference is not None: namespaceprefix_ = self.reference_nsprefix_ + ':' if (UseCapturedNS_ and self.reference_nsprefix_) else '' showIndent(outfile, level, pretty_print) @@ -28074,6 +27540,11 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False, gds_collec value_ = self.gds_validate_string(value_, node, 'name') self.name = value_ self.name_nsprefix_ = child_.prefix + elif nodeName_ == 'docs': + obj_ = tableofcontentsNameType.factory(parent_object_=self) + obj_.build(child_, gds_collector_=gds_collector_) + self.docs = obj_ + obj_.original_tagname_ = 'docs' elif nodeName_ == 'reference': value_ = child_.text value_ = self.gds_parse_string(value_, node, 'reference') @@ -28387,17 +27858,13 @@ def main(): __all__ = [ "DoxygenType", "MemberType", - "argsstring", - "array", - "attributes", - "bitfield", "childnodeType", "codelineType", "compoundRefType", "compounddefType", - "declname", - "definition", - "defname", + "conceptCodePart", + "conceptDocPart", + "conceptParts", "descriptionType", "docAnchorType", "docBlockQuoteType", @@ -28451,14 +27918,12 @@ def main(): "docVarListEntryType", "docVariableListType", "docXRefSectType", - "edgelabel", "enumvalueType", "exportType", "exportsType", "graphType", "highlightType", "incType", - "label", "linkType", "linkedTextType", "listingType", @@ -28466,21 +27931,16 @@ def main(): "locationType", "memberRefType", "memberdefType", - "name", "nodeType", "paramType", - "qualifiedname", - "qualifier", - "read", "refTextType", "refType", "referenceType", "reimplementType", - "scope", "sectiondefType", "spType", "tableofcontentsKindType", + "tableofcontentsNameType", "tableofcontentsType", - "templateparamlistType", - "write" + "templateparamlistType" ] diff --git a/addon/doxmlparser/examples/metrics/metrics.py b/addon/doxmlparser/examples/metrics/metrics.py index da126834a57..cc0108d6468 100644 --- a/addon/doxmlparser/examples/metrics/metrics.py +++ b/addon/doxmlparser/examples/metrics/metrics.py @@ -36,8 +36,8 @@ def __init__(self): def print(self): numMethods = self.numPubMethods + self.numProMethods + self.numPriMethods numDocMethods = self.numDocPubMethods + self.numDocProMethods + self.numDocPriMethods - print("Metrics:"); - print("-----------------------------------"); + print("Metrics:") + print("-----------------------------------") if self.numClasses>0: print("Classes: {:=10} ({} documented)".format(self.numClasses,self.numDocClasses)) if self.numStructs>0: @@ -70,12 +70,12 @@ def print(self): print("Variables: {:=10} ({} documented)".format(self.numVariables,self.numDocVariables)) if self.numParams>0: print("Params: {:=10}".format(self.numParams)) - print("-----------------------------------"); + print("-----------------------------------") if self.numClasses>0: print("Avg. #methods/compound: {:=10}".format(float(numMethods)/float(self.numClasses))) if numMethods>0: print("Avg. #params/method: {:=10}".format(float(self.numParams)/float(numMethods))) - print("-----------------------------------"); + print("-----------------------------------") def description_is_empty(description): diff --git a/addon/doxmlparser/generateDS_post.py b/addon/doxmlparser/generateDS_post.py index bca8906f657..5ed0b5f9df1 100755 --- a/addon/doxmlparser/generateDS_post.py +++ b/addon/doxmlparser/generateDS_post.py @@ -17,7 +17,6 @@ def main(): inputFile = open(sys.argv[1], 'r') outputFile = open(sys.argv[2], 'wb') - lineStr = "" for line in inputFile: line = line.rstrip() line = re.sub(r'##','# #',line) diff --git a/addon/doxyapp/doxyapp.cpp b/addon/doxyapp/doxyapp.cpp index c7bed208852..c4f51325123 100644 --- a/addon/doxyapp/doxyapp.cpp +++ b/addon/doxyapp/doxyapp.cpp @@ -54,6 +54,10 @@ class XRefDummyCodeGenerator : public OutputCodeIntf OutputType type() const override { return OutputType::Extension; } std::unique_ptr clone() override { return std::make_unique(m_fd); } void codify(const QCString &) override {} + void stripCodeComments(bool) override {} + void startSpecialComment() override {} + void endSpecialComment() override {} + void setStripIndentAmount(size_t) override {} void writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &) override {} void writeLineNumber(const QCString &,const QCString &,const QCString &,int,bool) override {} virtual void writeTooltip(const QCString &,const DocLinkInfo &, @@ -134,8 +138,8 @@ static void findXRefSymbols(FileDef *fd) fileToString(fd->absFilePath()), lang, FALSE, - QCString(), - fd); + CodeParserOptions() + .setFileDef(fd)); } static void listSymbol(Definition *d) diff --git a/addon/doxycommentview/README.md b/addon/doxycommentview/README.md new file mode 100644 index 00000000000..d6242230682 --- /dev/null +++ b/addon/doxycommentview/README.md @@ -0,0 +1,38 @@ +# Viewer for the content of a Doxygen style comment block + +## Contents + +The directory contains an index.html page and a python3 helper script. +The script can be used to start a local web server that can do life rendering of +the content of a doxygen comment block. + +Similar to e.g. https://markdownlivepreview.com/ but using doxygen as render engine. + +## To prepare the server + +Place a doxygen.css in the same directory as the doxycommentview.py script. + +This file can be generated running + + doxygen -w html /tmp/header.html /tmp/footer.html doxygen.css path/to/Doxyfile + +or, alternatively, copied from an existing HTML output directory generated by doxygen. + +## To run the server invoke: + + python3 doxycommentview.py --doxyfile /path/to/Doxyfile + +The relevant settings, such as alias definitions, will be taken from the Doxyfile. + +If desired you can set the port for the webserver using `--port` and +point to the location of the doxygen binary using `--doxygen` + +Once the server is started, point your browser to the index page + + firefox http://localhost:8000/index.html + +You should see a panel to enter text on the left hand side and the output +rendered by doxygen on the right hand side of the page. + +You can copy and paste the contents of this README.md file to test it quickly. + diff --git a/addon/doxycommentview/doxycommentview.py b/addon/doxycommentview/doxycommentview.py new file mode 100644 index 00000000000..3598b9a1a72 --- /dev/null +++ b/addon/doxycommentview/doxycommentview.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# python3 helper script to start a web server that can do life rendering of doxygen comments. +# +# Copyright (C) 1997-2024 by Dimitri van Heesch. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation under the terms of the GNU General Public License is hereby +# granted. No representations are made about the suitability of this software +# for any purpose. It is provided "as is" without express or implied warranty. +# See the GNU General Public License for more details. +# +# Documents produced by Doxygen are derivative works derived from the +# input used in their production; they are not affected by this license. +# + +import http.server +import socketserver +import json +import subprocess +import argparse +import signal +import threading +import html + +def main(): + # Set up argument parser + parser = argparse.ArgumentParser(description="Runs the doxygen comment viewer HTTP server.") + parser.add_argument('--port', type=int, default=8000, help='Port number to run the server on') + parser.add_argument('--doxygen', type=str, default='doxygen', help='Path to doxygen executable') + parser.add_argument('--doxyfile', type=str, default='Doxyfile', help='Path to Doxyfile to use') + args = parser.parse_args() + + PORT = args.port + DOXYGEN = args.doxygen + DOXYFILE = args.doxyfile + VERSION_STR = subprocess.run([DOXYGEN, '-v'], capture_output=True, text=True, encoding="utf-8").stdout + + class RequestHandler(http.server.SimpleHTTPRequestHandler): + def do_POST(self): + if self.path == '/process': + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + data = json.loads(post_data) + input_text = data['input'] + + # Run doxygen in single comment mode, reading from stdin and writing to stdout and stderr + result = subprocess.run([DOXYGEN, '-c', '-', DOXYFILE], \ + input=input_text, capture_output=True, text=True, encoding="utf-8") + + # Prepare the response + response = json.dumps({ + 'html_output': result.stdout, + 'error_output': "Doxygen version "+html.escape(VERSION_STR)+"
"+html.escape(result.stderr)+"
" + }) + + # Send the result to the requesting HTML page + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(response.encode()) + + httpd = socketserver.TCPServer(("", PORT), RequestHandler) + + def signal_handler(sig, frame): + print('Shutting down the web server...') + threading.Thread(target=httpd.shutdown).start() + + signal.signal(signal.SIGINT, signal_handler) + + print("Running web server on port", PORT) + httpd.serve_forever() + +if __name__ == '__main__': + main() diff --git a/addon/doxycommentview/index.html b/addon/doxycommentview/index.html new file mode 100644 index 00000000000..1ed6c8a9728 --- /dev/null +++ b/addon/doxycommentview/index.html @@ -0,0 +1,132 @@ + + + + + Doxygen comment viewer + + + + +
+
1
+ +
+
+ +
+ +
+
+ +
+
+ + + diff --git a/addon/doxyparse/doxyparse.cpp b/addon/doxyparse/doxyparse.cpp index bfbf872e7d4..a696845b748 100644 --- a/addon/doxyparse/doxyparse.cpp +++ b/addon/doxyparse/doxyparse.cpp @@ -18,7 +18,6 @@ * */ -#include #if !defined(_WIN32) || defined(__CYGWIN__) #include #else @@ -41,6 +40,8 @@ #include #include #include +#include +#include #include "qcstring.h" #include "namespacedef.h" #include "portable.h" @@ -57,6 +58,10 @@ class Doxyparse : public OutputCodeIntf OutputType type() const override { return OutputType::Extension; } std::unique_ptr clone() override { return std::make_unique(m_fd); } void codify(const QCString &) override {} + void stripCodeComments(bool) override {} + void startSpecialComment() override {} + void endSpecialComment() override {} + void setStripIndentAmount(size_t) override {} void writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &) override {} void startCodeLine(int) override {} void endCodeLine() override {} @@ -105,25 +110,16 @@ static void findXRefSymbols(FileDef *fd) parseList.add(std::move(parse)); // parse the source code - intf->parseCode(parseList, QCString(), fileToString(fd->absFilePath()), lang, FALSE, QCString(), fd); + intf->parseCode(parseList, QCString(), fileToString(fd->absFilePath()), lang, + FALSE, CodeParserOptions().setFileDef(fd)); } static bool ignoreStaticExternalCall(const MemberDef *context, const MemberDef *md) { - if (md->isStatic()) { - if(md->getFileDef() && context->getFileDef()) { - if(md->getFileDef()->getOutputFileBase() == context->getFileDef()->getOutputFileBase()) - // TODO ignore prefix of file - return false; - else - return true; - } - else { - return false; - } - } - else { - return false; - } + if (!md->isStatic()) return false; + if (!md->getFileDef() || !context->getFileDef()) return false; + + // TODO ignore prefix of file + return md->getFileDef()->getOutputFileBase() != context->getFileDef()->getOutputFileBase(); } static void startYamlDocument() { @@ -180,11 +176,13 @@ static int isPartOfCStruct(const MemberDef * md) { return is_c_code && md->getClassDef() != nullptr; } -std::string sanitizeString(std::string data) { - QCString new_data = QCString(data.c_str()); - new_data = substitute(new_data,"\"", ""); - new_data = substitute(new_data,"\'", ""); // https://github.com/analizo/analizo/issues/138 - return !new_data.isEmpty() ? new_data.data() : ""; +[[nodiscard]] std::string sanitizeString(std::string data) +{ + data.erase(std::remove_if(data.begin(), data.end(), [](unsigned char c) { + return c == '"' || c == '\''; + }), + data.end()); + return data; } std::string argumentData(const Argument &argument) { @@ -531,9 +529,7 @@ int main(int argc,char **argv) { startYamlDocument(); listSymbols(); - std::string cleanup_command = "rm -rf "; - cleanup_command += tmpdir.str(); - (void)system(cleanup_command.c_str()); - - exit(0); + std::error_code ec; + std::filesystem::remove_all(tmpdir.str(), ec); + return 0; } diff --git a/addon/doxypysql/search.py b/addon/doxypysql/search.py index 70feec2db0f..d973c764dd0 100755 --- a/addon/doxypysql/search.py +++ b/addon/doxypysql/search.py @@ -49,7 +49,7 @@ def re_fn(expr, item): return reg.search(item) is not None def openDb(dbname): - if dbname == None: + if dbname is None: dbname = "doxygen_sqlite3.db" if not os.path.isfile(dbname): @@ -71,7 +71,7 @@ def match(self,row): if self.row_type is int: return " rowid=?" else: - if g_use_regexp == True: + if g_use_regexp: return " REGEXP (?,%s)" %row else: return " %s=?" %row @@ -380,7 +380,7 @@ def serveCli(argv): cn=openDb(dbname) f=Finder(cn,o) - if ref != None: + if ref is not None: j=processHref(cn,ref) else: j=process(f,kind) diff --git a/addon/doxysearch/CMakeLists.txt b/addon/doxysearch/CMakeLists.txt index 37f48c21045..8ca1d2b01c3 100644 --- a/addon/doxysearch/CMakeLists.txt +++ b/addon/doxysearch/CMakeLists.txt @@ -1,8 +1,14 @@ -find_package(xapian REQUIRED) +find_package(XAPIAN REQUIRED) find_package(ZLIB REQUIRED) -if (WIN32) - set(WIN_EXTRA_LIBS uuid.lib rpcrt4.lib ws2_32.lib) +if(WIN32) + set(EXTRA_LIBS uuid rpcrt4 ws2_32) +elseif(static_libxapian) + set(EXTRA_LIBS uuid) + if(UUID_LIB) + add_library(uuid STATIC IMPORTED) + set_target_properties(uuid PROPERTIES IMPORTED_LOCATION "${UUID_LIB}") + endif() endif() include_directories( @@ -17,9 +23,10 @@ add_executable(doxyindexer ) target_link_libraries(doxyindexer + PRIVATE ${XAPIAN_LIBRARIES} ${ZLIB_LIBRARIES} - ${WIN_EXTRA_LIBS} + ${EXTRA_LIBS} ${COVERAGE_LINKER_FLAGS} doxygen_version xml @@ -31,10 +38,11 @@ add_executable(doxysearch.cgi ) target_link_libraries(doxysearch.cgi + PRIVATE doxygen_version ${XAPIAN_LIBRARIES} ${ZLIB_LIBRARIES} - ${WIN_EXTRA_LIBS} + ${EXTRA_LIBS} ) include(ApplyEditbin) diff --git a/addon/doxysearch/doxyindexer.cpp b/addon/doxysearch/doxyindexer.cpp index bac8cc785c7..c5cef3c6106 100644 --- a/addon/doxysearch/doxyindexer.cpp +++ b/addon/doxysearch/doxyindexer.cpp @@ -22,8 +22,9 @@ #include #include #include +#include +#include -#include // Xapian include #include @@ -104,7 +105,7 @@ static void addWords(const std::string &s,Xapian::Document &doc,int wfd) /** Adds all identifiers in \a s to document \a doc with weight \a wfd */ static void addIdentifiers(const std::string &s,Xapian::Document &doc,int wfd) { - std::regex id_re("[A-Z_a-z][A-Z_a-z0-9]*"); + static const std::regex id_re("[A-Z_a-z][A-Z_a-z0-9]*"); auto id_begin = std::sregex_iterator(s.begin(), s.end(), id_re); auto id_end = std::sregex_iterator(); @@ -176,18 +177,21 @@ class XMLContentHandler void startElement(const std::string &name, const XMLHandlers::Attributes &attrib) { m_data=""; - if (name=="field") - { - std::string fieldName = XMLHandlers::value(attrib,"name"); - if (fieldName=="type") m_curFieldName=TypeField; - else if (fieldName=="name") m_curFieldName=NameField; - else if (fieldName=="args") m_curFieldName=ArgsField; - else if (fieldName=="tag") m_curFieldName=TagField; - else if (fieldName=="url") m_curFieldName=UrlField; - else if (fieldName=="keywords") m_curFieldName=KeywordField; - else if (fieldName=="text") m_curFieldName=TextField; - else m_curFieldName=UnknownField; - } + + if (name != "field") return; + + static const std::unordered_map fieldMap{ + { "type", TypeField }, + { "name", NameField }, + { "args", ArgsField }, + { "tag", TagField }, + { "url", UrlField }, + { "keywords", KeywordField }, + { "text", TextField } + }; + std::string fieldName = XMLHandlers::value(attrib, "name"); + auto it = fieldMap.find(fieldName); + m_curFieldName = (it != fieldMap.end()) ? it->second : UnknownField; } /** Handler for an end tag. Called for `` and `` tags */ @@ -292,7 +296,9 @@ inline std::string fileToString(const std::string &fileName) std::ifstream t(fileName); std::string result; t.seekg(0, std::ios::end); - result.reserve(t.tellg()); + auto size = t.tellg(); + if (size < 0) size = 0; + result.reserve(static_cast(size)); t.seekg(0, std::ios::beg); result.assign(std::istreambuf_iterator(t), std::istreambuf_iterator()); @@ -301,8 +307,8 @@ inline std::string fileToString(const std::string &fileName) bool dirExists(const char *path) { - struct stat info = {}; - return stat(path,&info)==0 && (info.st_mode&S_IFDIR); + std::error_code ec; + return std::filesystem::is_directory(path, ec); } /** main function to index data */ @@ -315,7 +321,8 @@ int main(int argc,const char **argv) std::string outputDir; for (int i=1;i=argc-1) { @@ -333,11 +340,11 @@ int main(int argc,const char **argv) } } } - else if (std::string(argv[i])=="-h" || std::string(argv[i])=="--help") + else if (arg == "-h" || arg == "--help") { usage(argv[0],0); } - else if (std::string(argv[i])=="-v" || std::string(argv[i])=="--version") + else if (arg == "-v" || arg == "--version") { std::cerr << argv[0] << " version: " << getFullVersion() << std::endl; exit(0); diff --git a/addon/doxysearch/doxysearch.cpp b/addon/doxysearch/doxysearch.cpp index 0b0724b9d72..1e91c59c2ac 100644 --- a/addon/doxysearch/doxysearch.cpp +++ b/addon/doxysearch/doxysearch.cpp @@ -20,53 +20,37 @@ #include #include #include -#include #include #include +#include // Xapian includes #include #include "version.h" -#ifdef _WIN32 -#include -#else -#include -#endif - -#define FIELD_TYPE 1 -#define FIELD_NAME 2 -#define FIELD_ARGS 3 -#define FIELD_TAG 4 -#define FIELD_URL 5 -#define FIELD_KEYW 6 -#define FIELD_DOC 7 -#define HEX2DEC(x) (((x)>='0' && (x)<='9')?((x)-'0'):\ - ((x)>='a' && (x)<='f')?((x)-'a'+10):\ - ((x)>='A' && (x)<='F')?((x)-'A'+10):-1) +constexpr Xapian::valueno FIELD_TYPE{ 1 }; +constexpr Xapian::valueno FIELD_NAME{ 2 }; +constexpr Xapian::valueno FIELD_ARGS{ 3 }; +constexpr Xapian::valueno FIELD_TAG{ 4 }; +constexpr Xapian::valueno FIELD_URL{ 5 }; +constexpr Xapian::valueno FIELD_KEYW{ 6 }; +constexpr Xapian::valueno FIELD_DOC{ 7 }; - -bool dirExists(const std::string& dirName) +constexpr int hex2dec(unsigned char c) { -#ifdef _WIN32 - DWORD ftyp = GetFileAttributesA(dirName.c_str()); - if (ftyp == INVALID_FILE_ATTRIBUTES) - return false; //something is wrong with your path! - - if (ftyp & FILE_ATTRIBUTE_DIRECTORY) - return true; // this is a directory! -#else - struct stat sb; + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} - if (stat(dirName.c_str(), &sb)==0 && S_ISDIR(sb.st_mode)) - { - return true; - } -#endif - return false; +bool dirExists(const std::string &dirname) +{ + std::error_code ec; + return std::filesystem::is_directory(dirname, ec); } @@ -90,11 +74,10 @@ static std::string uriDecode(const std::string & sSrc) { if (*pSrc == '%') // replace %2A with corresponding ASCII character { - char dec1, dec2; + int dec1, dec2; unsigned char c1=*(pSrc+1); unsigned char c2=*(pSrc+2); - if (-1 != (dec1 = HEX2DEC(c1)) - && -1 != (dec2 = HEX2DEC(c2))) + if (-1 != (dec1 = hex2dec(c1)) && -1 != (dec2 = hex2dec(c2))) { *pEnd++ = (dec1 << 4) + dec2; pSrc += 3; @@ -359,7 +342,7 @@ int main(int argc,char **argv) std::cout << "Content-Type:application/javascript;charset=utf-8\r\n\n"; // parse query string std::vector parts = split(queryString,'&'); - std::string searchFor,callback; + std::string searchFor; int num=1,page=0; for (std::vector::const_iterator it=parts.begin();it!=parts.end();++it) { diff --git a/addon/doxywizard/CMakeLists.txt b/addon/doxywizard/CMakeLists.txt index a019e7fa159..d3a8cf1091f 100644 --- a/addon/doxywizard/CMakeLists.txt +++ b/addon/doxywizard/CMakeLists.txt @@ -1,49 +1,40 @@ -if (force_qt STREQUAL "Qt5" OR NOT force_qt) - find_package(Qt5Core QUIET CONFIG) - if (Qt5Core_FOUND) - message(STATUS "Using Qt5") - find_package(Qt5 COMPONENTS Widgets Gui Xml) - macro(qt_wrap_cpp) - qt5_wrap_cpp(${ARGN}) - endmacro() - macro(qt_add_resources) - qt5_add_resources(${ARGN}) - endmacro() - else() - if (NOT force_qt) - find_package(Qt6Core QUIET CONFIG) - if (Qt6Core_FOUND) - message(STATUS "Using Qt6") - find_package(Qt6 COMPONENTS Widgets Gui Xml) - macro(qt_wrap_cpp) - qt6_wrap_cpp(${ARGN}) - endmacro() - macro(qt_add_resources) - qt6_add_resources(${ARGN}) - endmacro() - else() - message(FATAL_ERROR "Qt5 nor Qt6 found") - endif() - else() - message(FATAL_ERROR "Qt5 not found") - endif() - endif() -else() +# Try finding Qt6 +if (force_qt STREQUAL "Qt6" OR NOT force_qt) find_package(Qt6Core QUIET CONFIG) if (Qt6Core_FOUND) message(STATUS "Using Qt6") - find_package(Qt6 COMPONENTS Widgets Gui Xml) + find_package(Qt6 REQUIRED COMPONENTS Widgets Gui Xml Svg) macro(qt_wrap_cpp) qt6_wrap_cpp(${ARGN}) endmacro() macro(qt_add_resources) qt6_add_resources(${ARGN}) endmacro() - else() + elseif (force_qt STREQUAL "Qt6") + # no fallback to Qt5 message(FATAL_ERROR "Qt6 not found") endif() endif() +# Try finding Qt5 +if (force_qt STREQUAL "Qt5" OR NOT Qt6_FOUND) + find_package(Qt5Core QUIET CONFIG) + if (Qt5Core_FOUND) + message(STATUS "Using Qt5") + find_package(Qt5 REQUIRED COMPONENTS Widgets Gui Xml Svg) + macro(qt_wrap_cpp) + qt5_wrap_cpp(${ARGN}) + endmacro() + macro(qt_add_resources) + qt5_add_resources(${ARGN}) + endmacro() + elseif (force_qt STREQUAL "Qt5") + message(FATAL_ERROR "Qt5 not found") + else() + message(FATAL_ERROR "Qt5 nor Qt6 found") + endif() +endif() + include_directories( . ${PROJECT_SOURCE_DIR}/libversion @@ -182,12 +173,10 @@ endif() if(Qt5Core_FOUND) target_link_libraries(doxywizard Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml doxygen_version) -else() -if(Qt6Core_FOUND) +elseif(Qt6Core_FOUND) target_link_libraries(doxywizard Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Xml doxygen_version) else() target_link_libraries(doxywizard ${QT_LIBRARIES} ${QT_QTMAIN_LIBRARY} doxygen_version) endif() -endif() install(TARGETS doxywizard DESTINATION bin) diff --git a/addon/doxywizard/config.h b/addon/doxywizard/config.h index 13c997397b2..a905175ab6d 100644 --- a/addon/doxywizard/config.h +++ b/addon/doxywizard/config.h @@ -26,7 +26,7 @@ bool parseConfig( const QHash &options ); -void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s); +void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s,bool convert); // directly copied from ../../src/config.h to be consistent enum diff --git a/addon/doxywizard/config_doxyw.l b/addon/doxywizard/config_doxyw.l index 4a7fc6de776..e096c6a5e94 100644 --- a/addon/doxywizard/config_doxyw.l +++ b/addon/doxywizard/config_doxyw.l @@ -19,7 +19,7 @@ %{ /* - * includes + * includes */ #include "config.h" #include "input.h" @@ -46,7 +46,7 @@ /* ----------------------------------------------------------------- * - * static variables + * static variables */ struct ConfigFileState @@ -77,8 +77,8 @@ static const char *stateToString(int state); /* ----------------------------------------------------------------- */ -#undef YY_INPUT -#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); static int yyread(char *buf,int maxSize) { @@ -236,6 +236,10 @@ static void processList() { elemStr+='"'; } + else if (insideQuote && i &options) bool classGraph = InputBool::convertToBool(v3,isValid3); if (isValid1 && isValid2 && isValid3 && !classDiagrams && !haveDot && classGraph) { - config_warn("Changing CLASS_GRAPH option to TEXT because obsolete option CLASS_DIAGRAM was found and set to NO.\n"); + config_warn("Changing CLASS_GRAPH option to TEXT because obsolete option CLASS_DIAGRAM was found and set to NO.\n"); optClassGraph->setValue(QString::fromLatin1("TEXT")); } } @@ -785,7 +789,7 @@ bool parseConfig( return true; } -void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s) +void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s,bool convert) { QChar c; bool needsEscaping=false; @@ -800,10 +804,11 @@ void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s) while (!(c=*p++).isNull() && !needsEscaping) { needsEscaping = (c==QChar::fromLatin1(' ') || - c==QChar::fromLatin1(',') || - c==QChar::fromLatin1('\n') || - c==QChar::fromLatin1('\t') || - c==QChar::fromLatin1('"')); + c==QChar::fromLatin1(',') || + c==QChar::fromLatin1('\n') || + c==QChar::fromLatin1('\t') || + c==QChar::fromLatin1('\\') || + c==QChar::fromLatin1('"')); } p=s.data(); while (!(c=*p++).isNull() && !needsHashEscaping) @@ -820,15 +825,42 @@ void writeStringValue(QTextStream &t,TextCodecAdapter *codec,const QString &s) p=s.data(); while (!p->isNull()) { - if (*p ==QChar::fromLatin1(' ') && - *(p+1)==QChar::fromLatin1('\0')) break; // skip inserted space at the end - if (*p ==QChar::fromLatin1('"')) t << "\\"; // escape quotes - t << *p++; + if (*p ==QChar::fromLatin1(' ') && + *(p+1)==QChar::fromLatin1('\0')) break; // skip inserted space at the end + if (*p ==QChar::fromLatin1('"')) t << "\\"; // escape quotes + if (*p ==QChar::fromLatin1('\\') && (p+1)->isNull()) t << "\\"; // escape last "\", for e.g c:\temp\ where "\" is part of the path + if (convert) + { + if (*p ==QChar::fromLatin1('<')) t << "<"; + else if (*p ==QChar::fromLatin1('>')) t << ">"; + else if (*p ==QChar::fromLatin1('&')) t << "&"; + else t << *p; + } + else + { + t << *p; + } + p++; } } else { - t << s; + p=s.data(); + while (!p->isNull()) + { + if (convert) + { + if (*p ==QChar::fromLatin1('<')) t << "<"; + else if (*p ==QChar::fromLatin1('>')) t << ">"; + else if (*p ==QChar::fromLatin1('&')) t << "&"; + else t << *p; + } + else + { + t << *p; + } + p++; + } } if (needsHashEscaping || needsEscaping) { diff --git a/addon/doxywizard/doxywizard.cpp b/addon/doxywizard/doxywizard.cpp index be6b598f2ae..f114d79a380 100644 --- a/addon/doxywizard/doxywizard.cpp +++ b/addon/doxywizard/doxywizard.cpp @@ -63,14 +63,14 @@ MainWindow::MainWindow() { QMenu *file = menuBar()->addMenu(tr("File")); file->addAction(tr("Open..."), - this, SLOT(openConfig()), Qt::CTRL|Qt::Key_O); + this, SLOT(openConfig()), QKeySequence{ Qt::CTRL | Qt::Key_O }); m_recentMenu = file->addMenu(tr("Open recent")); file->addAction(tr("Save"), - this, SLOT(saveConfig()), Qt::CTRL|Qt::Key_S); + this, SLOT(saveConfig()), QKeySequence{ Qt::CTRL | Qt::Key_S }); file->addAction(tr("Save as..."), - this, SLOT(saveConfigAs()), Qt::SHIFT|Qt::CTRL|Qt::Key_S); + this, SLOT(saveConfigAs()), QKeySequence{ Qt::SHIFT | Qt::CTRL | Qt::Key_S }); file->addAction(tr("Quit"), - this, SLOT(quit()), Qt::CTRL|Qt::Key_Q); + this, SLOT(quit()), QKeySequence{ Qt::CTRL | Qt::Key_Q }); QMenu *settings = menuBar()->addMenu(tr("Settings")); m_resetDefault = settings->addAction(tr("Reset to factory defaults"), @@ -81,7 +81,7 @@ MainWindow::MainWindow() this,SLOT(clearRecent())); settings->addSeparator(); m_runMenu = settings->addAction(tr("Run doxygen"), - this,SLOT(runDoxygenMenu()),Qt::CTRL|Qt::Key_R); + this, SLOT(runDoxygenMenu()), QKeySequence{ Qt::CTRL | Qt::Key_R }); m_runMenu->setEnabled(false); QMenu *help = menuBar()->addMenu(tr("Help")); @@ -341,7 +341,7 @@ void MainWindow::saveConfig(const QString &fileName) } QTextStream t(&f); t.device()->setTextModeEnabled(false); - m_expert->writeConfig(t,false,false); + m_expert->writeConfig(t,false,false,false); updateConfigFileName(fileName); m_modified = false; updateTitle(); @@ -586,7 +586,7 @@ void MainWindow::runDoxygen() return; } QTextStream t(m_runProcess); - m_expert->writeConfig(t,false,false); + m_expert->writeConfig(t,false,false,false); t.flush(); m_runProcess->closeWriteChannel(); @@ -655,7 +655,7 @@ void MainWindow::runComplete() } else { - m_outputLog->append(APPQT(tr("*** Cancelled by user\n"))); + m_outputLog->append(APPQT(tr("*** Canceled by user\n"))); } m_outputLog->ensureCursorVisible(); m_run->setText(tr("Run doxygen")); @@ -706,8 +706,8 @@ void MainWindow::saveLog() } else { - QMessageBox::warning(nullptr,tr("Warning"), - tr("Cannot open file ")+fn+tr(" for writing. Nothing saved!"),tr("ok")); + QMessageBox::warning(nullptr, tr("Warning"), + tr("Cannot open file ") + fn + tr(" for writing. Nothing saved!"), QMessageBox::Ok); } } } @@ -718,11 +718,11 @@ void MainWindow::showSettings() QTextStream t(&text); if (m_showCondensedSettings->isChecked()) { - m_expert->writeConfig(t,true,true); + m_expert->writeConfig(t,true,true,true); } else { - m_expert->writeConfig(t,true,false); + m_expert->writeConfig(t,true,false,true); } m_outputLog->clear(); m_outputLog->append(APPQT(text)); @@ -807,13 +807,15 @@ void MainWindow::outputLogFinish() //----------------------------------------------------------------------- int main(int argc,char **argv) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) static const char ENV_VAR_QT_DEVICE_PIXEL_RATIO[] = "QT_DEVICE_PIXEL_RATIO"; if (!qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO) - && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") - && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") - && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { + && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } +#endif QApplication a(argc,argv); int locArgc = argc; diff --git a/addon/doxywizard/doxywizard.qrc b/addon/doxywizard/doxywizard.qrc index 88316eda643..7b9fa09b0ab 100644 --- a/addon/doxywizard/doxywizard.qrc +++ b/addon/doxywizard/doxywizard.qrc @@ -1,11 +1,12 @@ ../../src/config.xml - images/add.png - images/del.png - images/file.png - images/folder.png - images/refresh.png - images/tunecolor.png + images/add.svg + images/del.svg + images/file.svg + images/folder.svg + images/refresh.svg + images/tunecolor_light.png + images/tunecolor_dark.png diff --git a/addon/doxywizard/expert.cpp b/addon/doxywizard/expert.cpp index 59b4ed674b7..07b5253b69b 100644 --- a/addon/doxywizard/expert.cpp +++ b/addon/doxywizard/expert.cpp @@ -791,7 +791,7 @@ void Expert::loadConfig(const QString &fileName) } void Expert::saveTopic(QTextStream &t,QDomElement &elem,TextCodecAdapter *codec, - bool brief,bool condensed) + bool brief,bool condensed,bool convert) { if (!brief) { @@ -832,7 +832,7 @@ void Expert::saveTopic(QTextStream &t,QDomElement &elem,TextCodecAdapter *codec, if (option && !option->isEmpty()) { t << " "; - option->writeValue(t,codec); + option->writeValue(t,codec,convert); } t << "\n"; } @@ -843,7 +843,7 @@ void Expert::saveTopic(QTextStream &t,QDomElement &elem,TextCodecAdapter *codec, } } -bool Expert::writeConfig(QTextStream &t,bool brief, bool condensed) +bool Expert::writeConfig(QTextStream &t,bool brief, bool condensed, bool convert) { // write global header t << "# Doxyfile " << getDoxygenVersion().c_str() << "\n\n"; @@ -859,7 +859,7 @@ bool Expert::writeConfig(QTextStream &t,bool brief, bool condensed) { if (childElem.tagName()==SA("group")) { - saveTopic(t,childElem,&codec,brief,condensed); + saveTopic(t,childElem,&codec,brief,condensed,convert); } childElem = childElem.nextSiblingElement(); } diff --git a/addon/doxywizard/expert.h b/addon/doxywizard/expert.h index 8f0b367011c..df6a9da6c7a 100644 --- a/addon/doxywizard/expert.h +++ b/addon/doxywizard/expert.h @@ -38,7 +38,7 @@ class Expert : public QSplitter, public DocIntf void loadSettings(QSettings *); void saveSettings(QSettings *); void loadConfig(const QString &fileName); - bool writeConfig(QTextStream &t,bool brief,bool condensed); + bool writeConfig(QTextStream &t,bool brief,bool condensed, bool convert); QByteArray saveInnerState () const; bool restoreInnerState ( const QByteArray & state ); const QHash &modelData() const { return m_options; } @@ -67,7 +67,7 @@ class Expert : public QSplitter, public DocIntf private: void createTopics(const QDomElement &); - void saveTopic(QTextStream &t,QDomElement &elem,TextCodecAdapter *codec,bool brief,bool dondensed); + void saveTopic(QTextStream &t,QDomElement &elem,TextCodecAdapter *codec,bool brief,bool dondensed,bool convert); QSplitter *m_splitter; QTextBrowser *m_helper; diff --git a/addon/doxywizard/images/add.eps b/addon/doxywizard/images/add.eps new file mode 100644 index 00000000000..f3156db0ee0 --- /dev/null +++ b/addon/doxywizard/images/add.eps @@ -0,0 +1,117 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.18.0 (https://cairographics.org) +%%CreationDate: Sat Oct 18 17:29:18 2025 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 18 19 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 18 19 +%%EndPageSetup +q 0 1 18 18 rectclip +1 0 0 -1 0 19 cm q +q +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 1 ] + /C1 [ 0.6 0.6 0.6 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 2 2 2 22 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +0.6 g +1.125 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m S Q +0.266667 g +9 4.875 m 9.414 4.875 9.75 5.211 9.75 5.625 c 9.75 12.375 l 9.75 12.789 + 9.414 13.125 9 13.125 c 8.586 13.125 8.25 12.789 8.25 12.375 c 8.25 5.625 + l 8.25 5.211 8.586 4.875 9 4.875 c h +9 4.875 m f +5.625 8.25 m 12.375 8.25 l 12.789 8.25 13.125 8.586 13.125 9 c 13.125 9.414 + 12.789 9.75 12.375 9.75 c 5.625 9.75 l 5.211 9.75 4.875 9.414 4.875 9 c + 4.875 8.586 5.211 8.25 5.625 8.25 c h +5.625 8.25 m f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/addon/doxywizard/images/add.png b/addon/doxywizard/images/add.png deleted file mode 100644 index 3af7eb7d7f2..00000000000 Binary files a/addon/doxywizard/images/add.png and /dev/null differ diff --git a/addon/doxywizard/images/add.svg b/addon/doxywizard/images/add.svg new file mode 100644 index 00000000000..b52d17f2498 --- /dev/null +++ b/addon/doxywizard/images/add.svg @@ -0,0 +1,20 @@ + + + Add + + + + + + + + + + + + + + + diff --git a/addon/doxywizard/images/del.eps b/addon/doxywizard/images/del.eps new file mode 100644 index 00000000000..d71ef4ac84d --- /dev/null +++ b/addon/doxywizard/images/del.eps @@ -0,0 +1,113 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.18.0 (https://cairographics.org) +%%CreationDate: Sat Oct 18 17:30:49 2025 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 18 19 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 18 19 +%%EndPageSetup +q 0 1 18 18 rectclip +1 0 0 -1 0 19 cm q +q +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 1 ] + /C1 [ 0.6 0.6 0.6 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 2 2 2 22 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +0.6 g +1.125 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m S Q +0.266667 g +5.625 8.25 m 12.375 8.25 l 12.789 8.25 13.125 8.586 13.125 9 c 13.125 9.414 + 12.789 9.75 12.375 9.75 c 5.625 9.75 l 5.211 9.75 4.875 9.414 4.875 9 c + 4.875 8.586 5.211 8.25 5.625 8.25 c h +5.625 8.25 m f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/addon/doxywizard/images/del.png b/addon/doxywizard/images/del.png deleted file mode 100644 index c2e3bc8a403..00000000000 Binary files a/addon/doxywizard/images/del.png and /dev/null differ diff --git a/addon/doxywizard/images/del.svg b/addon/doxywizard/images/del.svg new file mode 100644 index 00000000000..c3ad510c627 --- /dev/null +++ b/addon/doxywizard/images/del.svg @@ -0,0 +1,19 @@ + + + Add + + + + + + + + + + + + + + diff --git a/addon/doxywizard/images/file.eps b/addon/doxywizard/images/file.eps new file mode 100644 index 00000000000..da7c7cdada0 --- /dev/null +++ b/addon/doxywizard/images/file.eps @@ -0,0 +1,132 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.18.0 (https://cairographics.org) +%%CreationDate: Sat Oct 18 17:31:00 2025 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 18 19 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 18 19 +%%EndPageSetup +q 0 1 18 18 rectclip +1 0 0 -1 0 19 cm q +q +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 1 ] + /C1 [ 0.6 0.6 0.6 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 2 2 2 22 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +0.6 g +1.125 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m S Q +0.266667 g +0.675 w +1 J +1 j +q 1 0 0 1 0 0 cm +6.375 5.625 m 9.977 5.625 l 11.625 7.273 l 11.625 12.375 l 6.375 12.375 + l h +6.375 5.625 m S Q +0.375 w +0 J +0 j +q 1 0 0 1 0 0 cm +10.051 5.699 m 10.051 7.273 l 11.551 7.273 l h +10.051 5.699 m S Q +0.675 w +1 J +1 j +q 1 0 0 1 0 0 cm +9.977 5.625 m 11.625 7.273 l S Q +0 j +q 1 0 0 1 0 0 cm +7.5 8.699 m 10.5 8.699 l S Q +q 1 0 0 1 0 0 cm +7.5 10.051 m 9.75 10.051 l S Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/addon/doxywizard/images/file.png b/addon/doxywizard/images/file.png deleted file mode 100644 index 14f2c951058..00000000000 Binary files a/addon/doxywizard/images/file.png and /dev/null differ diff --git a/addon/doxywizard/images/file.svg b/addon/doxywizard/images/file.svg new file mode 100644 index 00000000000..00807a196ed --- /dev/null +++ b/addon/doxywizard/images/file.svg @@ -0,0 +1,28 @@ + + + Document + + + + + + + + + + + + + + + + + + + + diff --git a/addon/doxywizard/images/folder.eps b/addon/doxywizard/images/folder.eps new file mode 100644 index 00000000000..68ac1dc8031 --- /dev/null +++ b/addon/doxywizard/images/folder.eps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.18.0 (https://cairographics.org) +%%CreationDate: Sat Oct 18 17:31:12 2025 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 18 19 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 18 19 +%%EndPageSetup +q 0 1 18 18 rectclip +1 0 0 -1 0 19 cm q +q +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 1 ] + /C1 [ 0.6 0.6 0.6 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 2 2 2 22 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +0.6 g +1.125 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m S Q +0.266667 g +0.75 w +1 J +1 j +q 1 0 0 1 0 0 cm +5.398 6.602 m 7.949 6.602 l 9 7.648 l 12.602 7.648 l 12.602 12.148 l 5.398 + 12.148 l h +5.398 6.602 m S Q +0.675 w +q 1 0 0 1 0 0 cm +6.148 9.449 m 11.25 9.449 l S Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/addon/doxywizard/images/folder.png b/addon/doxywizard/images/folder.png deleted file mode 100644 index c91c5827cb6..00000000000 Binary files a/addon/doxywizard/images/folder.png and /dev/null differ diff --git a/addon/doxywizard/images/folder.svg b/addon/doxywizard/images/folder.svg new file mode 100644 index 00000000000..37b908b7bbd --- /dev/null +++ b/addon/doxywizard/images/folder.svg @@ -0,0 +1,22 @@ + + + Folder + + + + + + + + + + + + + + + + + diff --git a/addon/doxywizard/images/refresh.eps b/addon/doxywizard/images/refresh.eps new file mode 100644 index 00000000000..a892c65f13c --- /dev/null +++ b/addon/doxywizard/images/refresh.eps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.18.0 (https://cairographics.org) +%%CreationDate: Sat Oct 18 17:31:23 2025 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 18 19 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 18 19 +%%EndPageSetup +q 0 1 18 18 rectclip +1 0 0 -1 0 19 cm q +q +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 1 ] + /C1 [ 0.6 0.6 0.6 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 2 2 2 22 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +0.6 g +1.125 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +16.5 9 m 16.5 13.141 13.141 16.5 9 16.5 c 4.859 16.5 1.5 13.141 1.5 9 c + 1.5 4.859 4.859 1.5 9 1.5 c 13.141 1.5 16.5 4.859 16.5 9 c h +16.5 9 m S Q +0.266667 g +1.2 w +1 j +q 1 0 0 1 0 0 cm +5.852 9 m 5.852 7.262 7.262 5.852 9 5.852 c S Q +q 1 0 0 1 0 0 cm +12.148 9 m 12.148 10.738 10.738 12.148 9 12.148 c S Q +11.102 6 m 8.699 7.875 l 8.699 4.125 l h +11.102 6 m f +6.898 12.148 m 9.301 10.273 l 9.301 14.023 l h +6.898 12.148 m f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/addon/doxywizard/images/refresh.png b/addon/doxywizard/images/refresh.png deleted file mode 100644 index 413735bdb7c..00000000000 Binary files a/addon/doxywizard/images/refresh.png and /dev/null differ diff --git a/addon/doxywizard/images/refresh.svg b/addon/doxywizard/images/refresh.svg new file mode 100644 index 00000000000..09c0f1f4ae6 --- /dev/null +++ b/addon/doxywizard/images/refresh.svg @@ -0,0 +1,26 @@ + + + Refresh + + + + + + + + + + + + + + + + + + + + + diff --git a/addon/doxywizard/images/tunecolor.png b/addon/doxywizard/images/tunecolor.png deleted file mode 100644 index 9e595e46ad7..00000000000 Binary files a/addon/doxywizard/images/tunecolor.png and /dev/null differ diff --git a/addon/doxywizard/images/tunecolor_dark.png b/addon/doxywizard/images/tunecolor_dark.png new file mode 100644 index 00000000000..0254b43fd02 Binary files /dev/null and b/addon/doxywizard/images/tunecolor_dark.png differ diff --git a/addon/doxywizard/images/tunecolor_light.png b/addon/doxywizard/images/tunecolor_light.png new file mode 100644 index 00000000000..ca9abc0bb98 Binary files /dev/null and b/addon/doxywizard/images/tunecolor_light.png differ diff --git a/addon/doxywizard/input.h b/addon/doxywizard/input.h index 52d10ed4a42..fe762484bd0 100644 --- a/addon/doxywizard/input.h +++ b/addon/doxywizard/input.h @@ -42,7 +42,7 @@ class Input virtual void updateDependencies() = 0; virtual void reset() = 0; virtual bool isDefault() = 0; - virtual void writeValue(QTextStream &t,TextCodecAdapter *codec) = 0; + virtual void writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert) = 0; virtual void setTemplateDocs(const QString &docs) = 0; virtual bool isEmpty() { return false; }; }; diff --git a/addon/doxywizard/inputbool.cpp b/addon/doxywizard/inputbool.cpp index ad6894bef15..51c6bc60e36 100644 --- a/addon/doxywizard/inputbool.cpp +++ b/addon/doxywizard/inputbool.cpp @@ -134,7 +134,7 @@ void InputBool::reset() setValue(m_default); } -void InputBool::writeValue(QTextStream &t,TextCodecAdapter *codec) +void InputBool::writeValue(QTextStream &t,TextCodecAdapter *codec,bool) { if (m_state) t << codec->encode(QString::fromLatin1("YES")); diff --git a/addon/doxywizard/inputbool.h b/addon/doxywizard/inputbool.h index a17f67207f2..bb12835c7fe 100644 --- a/addon/doxywizard/inputbool.h +++ b/addon/doxywizard/inputbool.h @@ -39,7 +39,7 @@ class InputBool : public QObject, public Input void setEnabled(bool); void updateDependencies(); bool isDefault(); - void writeValue(QTextStream &t,TextCodecAdapter *codec); + void writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert); void setTemplateDocs(const QString &docs) { m_tdocs = docs; } static bool convertToBool(const QVariant &v,bool &isValid); diff --git a/addon/doxywizard/inputint.cpp b/addon/doxywizard/inputint.cpp index 2b3b2d6253b..6705c49bda8 100644 --- a/addon/doxywizard/inputint.cpp +++ b/addon/doxywizard/inputint.cpp @@ -123,7 +123,7 @@ void InputInt::reset() setValue(m_default); } -void InputInt::writeValue(QTextStream &t,TextCodecAdapter *) +void InputInt::writeValue(QTextStream &t,TextCodecAdapter *,bool) { t << m_val; } diff --git a/addon/doxywizard/inputint.h b/addon/doxywizard/inputint.h index 7dc0ae78d5b..91072b4cb96 100644 --- a/addon/doxywizard/inputint.h +++ b/addon/doxywizard/inputint.h @@ -42,7 +42,7 @@ class InputInt : public QObject, public Input void setEnabled(bool); void updateDependencies() {} bool isDefault(); - void writeValue(QTextStream &t,TextCodecAdapter *codec); + void writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert); void setTemplateDocs(const QString &docs) { m_tdocs = docs; } public slots: diff --git a/addon/doxywizard/inputobsolete.h b/addon/doxywizard/inputobsolete.h index d1d90a55e46..edc86f4a5a7 100644 --- a/addon/doxywizard/inputobsolete.h +++ b/addon/doxywizard/inputobsolete.h @@ -30,7 +30,7 @@ class InputObsolete : public Input void updateDependencies() {} void reset() {} bool isDefault() { return false; } - void writeValue(QTextStream &,TextCodecAdapter *) {} + void writeValue(QTextStream &,TextCodecAdapter *,bool) {} void setTemplateDocs(const QString &) {} bool isEmpty() { return false; }; Kind orgKind() const { return m_orgKind; } diff --git a/addon/doxywizard/inputstring.cpp b/addon/doxywizard/inputstring.cpp index ccb919fbb78..ef6dd105f31 100644 --- a/addon/doxywizard/inputstring.cpp +++ b/addon/doxywizard/inputstring.cpp @@ -69,7 +69,7 @@ InputString::InputString( QGridLayout *layout,int &row, m_br->setIconSize(QSize(24,24)); if (m==StringFile || m==StringImage || m==StringFileDir) { - m_brFile = m_br->addAction(QIcon(QString::fromLatin1(":/images/file.png")),QString(),this,SLOT(browseFile())); + m_brFile = m_br->addAction(QIcon(QString::fromLatin1(":/images/file.svg")),QString(),this,SLOT(browseFile())); m_brFile->setToolTip(tr("Browse to a file")); if (m==StringImage) { @@ -82,7 +82,7 @@ InputString::InputString( QGridLayout *layout,int &row, } if (m==StringDir || m==StringFileDir) { - m_brDir = m_br->addAction(QIcon(QString::fromLatin1(":/images/folder.png")),QString(),this,SLOT(browseDir())); + m_brDir = m_br->addAction(QIcon(QString::fromLatin1(":/images/folder.svg")),QString(),this,SLOT(browseDir())); m_brDir->setToolTip(tr("Browse to a folder")); } rowLayout->addWidget( m_br); @@ -253,9 +253,9 @@ void InputString::reset() setDefault(); } -void InputString::writeValue(QTextStream &t,TextCodecAdapter *codec) +void InputString::writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert) { - writeStringValue(t,codec,m_str); + writeStringValue(t,codec,m_str,convert); } bool InputString::isDefault() diff --git a/addon/doxywizard/inputstring.h b/addon/doxywizard/inputstring.h index 11a3fcd2f4f..ac8b713d14f 100644 --- a/addon/doxywizard/inputstring.h +++ b/addon/doxywizard/inputstring.h @@ -60,7 +60,7 @@ class InputString : public QObject, public Input void setEnabled(bool); void updateDependencies() {} bool isDefault(); - void writeValue(QTextStream &t,TextCodecAdapter *codec); + void writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert); void setTemplateDocs(const QString &docs) { m_tdocs = docs; } bool isEmpty() { return m_str.isEmpty(); } QString checkEnumVal(const QString &value); diff --git a/addon/doxywizard/inputstrlist.cpp b/addon/doxywizard/inputstrlist.cpp index f30d2a629e9..0c277b12c9e 100644 --- a/addon/doxywizard/inputstrlist.cpp +++ b/addon/doxywizard/inputstrlist.cpp @@ -36,13 +36,13 @@ InputStrList::InputStrList( QGridLayout *layout,int &row, QToolBar *toolBar = new QToolBar; toolBar->setIconSize(QSize(24,24)); - m_add = toolBar->addAction(QIcon(QString::fromLatin1(":/images/add.png")),QString(), + m_add = toolBar->addAction(QIcon(QString::fromLatin1(":/images/add.svg")),QString(), this,SLOT(addString())); m_add->setToolTip(tr("Add item")); - m_del = toolBar->addAction(QIcon(QString::fromLatin1(":/images/del.png")),QString(), + m_del = toolBar->addAction(QIcon(QString::fromLatin1(":/images/del.svg")),QString(), this,SLOT(delString())); m_del->setToolTip(tr("Delete selected item")); - m_upd = toolBar->addAction(QIcon(QString::fromLatin1(":/images/refresh.png")),QString(), + m_upd = toolBar->addAction(QIcon(QString::fromLatin1(":/images/refresh.svg")),QString(), this,SLOT(updateString())); m_upd->setToolTip(tr("Update selected item")); @@ -56,13 +56,13 @@ InputStrList::InputStrList( QGridLayout *layout,int &row, { if (lm&ListFile) { - m_brFile = toolBar->addAction(QIcon(QString::fromLatin1(":/images/file.png")),QString(), + m_brFile = toolBar->addAction(QIcon(QString::fromLatin1(":/images/file.svg")),QString(), this,SLOT(browseFiles())); m_brFile->setToolTip(tr("Browse to a file")); } if (lm&ListDir) { - m_brDir = toolBar->addAction(QIcon(QString::fromLatin1(":/images/folder.png")),QString(), + m_brDir = toolBar->addAction(QIcon(QString::fromLatin1(":/images/folder.svg")),QString(), this,SLOT(browseDir())); m_brDir->setToolTip(tr("Browse to a folder")); } @@ -242,7 +242,7 @@ void InputStrList::reset() setValue(m_default); } -void InputStrList::writeValue(QTextStream &t,TextCodecAdapter *codec) +void InputStrList::writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert) { bool first=true; foreach (QString s, m_strList) @@ -253,7 +253,7 @@ void InputStrList::writeValue(QTextStream &t,TextCodecAdapter *codec) t << " "; } first=false; - writeStringValue(t,codec,s); + writeStringValue(t,codec,s,convert); } } diff --git a/addon/doxywizard/inputstrlist.h b/addon/doxywizard/inputstrlist.h index 6d34061aa15..2bd0d23c97f 100644 --- a/addon/doxywizard/inputstrlist.h +++ b/addon/doxywizard/inputstrlist.h @@ -51,7 +51,7 @@ class InputStrList : public QObject, public Input void setEnabled(bool); void updateDependencies() {} bool isDefault(); - void writeValue(QTextStream &t,TextCodecAdapter *codec); + void writeValue(QTextStream &t,TextCodecAdapter *codec,bool convert); void setTemplateDocs(const QString &docs) { m_tdocs = docs; } bool isEmpty(); diff --git a/addon/doxywizard/wizard.cpp b/addon/doxywizard/wizard.cpp index 7adf3031bce..212f87a3ad6 100644 --- a/addon/doxywizard/wizard.cpp +++ b/addon/doxywizard/wizard.cpp @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright (C) 1997-2019 by Dimitri van Heesch. + * Copyright (C) 1997-2025 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby @@ -183,11 +183,16 @@ TuneColorDialog::TuneColorDialog(int hue,int sat,int gamma,QWidget *parent) : QD { setWindowTitle(tr("Tune the color of the HTML output")); QGridLayout *layout = new QGridLayout(this); - m_image = new QImage(QString::fromLatin1(":/images/tunecolor.png")); - m_imageLab = new QLabel; + m_imageLight = QImage(QString::fromLatin1(":/images/tunecolor_light.png")); + m_imageDark = QImage(QString::fromLatin1(":/images/tunecolor_dark.png")); + m_tab = new QTabWidget; + m_imageLabLight = new QLabel; + m_imageLabDark = new QLabel; + m_tab->addTab(m_imageLabLight,QString::fromLatin1("light")); + m_tab->addTab(m_imageLabDark,QString::fromLatin1("dark")); updateImage(hue,sat,gamma); layout->addWidget(new QLabel(tr("Example output: use the sliders on the right to adjust the color")),0,0); - layout->addWidget(m_imageLab,1,0); + layout->addWidget(m_tab,1,0); QHBoxLayout *buttonsLayout = new QHBoxLayout; QPushButton *okButton = new QPushButton(tr("Ok")); @@ -218,6 +223,7 @@ TuneColorDialog::TuneColorDialog(int hue,int sat,int gamma,QWidget *parent) : QD connect(huePicker,SIGNAL(newHsv(int,int,int)),this,SLOT(updateImage(int,int,int))); connect(satPicker,SIGNAL(newHsv(int,int,int)),this,SLOT(updateImage(int,int,int))); connect(gamPicker,SIGNAL(newHsv(int,int,int)),this,SLOT(updateImage(int,int,int))); + connect(m_tab, SIGNAL(currentChanged(int)),this,SLOT(tabChanged(int))); buttonsLayout->addStretch(); buttonsLayout->addWidget(okButton); @@ -225,31 +231,24 @@ TuneColorDialog::TuneColorDialog(int hue,int sat,int gamma,QWidget *parent) : QD layout->addLayout(buttonsLayout,5,0,1,4); } -void hsl2rgb(double h,double s,double l, - double *pRed,double *pGreen,double *pBlue) +// convert color in HSL color space to RGB +static constexpr void hsl2rgb(double h,double s,double l, + double *pRed,double *pGreen,double *pBlue) { - double v; - double r,g,b; - - r = l; // default to gray - g = l; - b = l; - v = (l <= 0.5) ? (l * (1.0 + s)) : (l + s - l * s); + double r = l; // default to gray + double g = l; + double b = l; + double v = (l <= 0.5) ? (l * (1.0 + s)) : (l + s - l * s); if (v > 0) { - double m; - double sv; - int sextant; - double fract, vsf, mid1, mid2; - - m = l + l - v; - sv = (v - m ) / v; - h *= 6.0; - sextant = (int)h; - fract = h - sextant; - vsf = v * sv * fract; - mid1 = m + vsf; - mid2 = v - vsf; + double m = l + l - v; + double sv = (v - m ) / v; + h *= 6.0; + int sextant = (int)h; + double fract = h - sextant; + double vsf = v * sv * fract; + double mid1 = m + vsf; + double mid2 = v - vsf; switch (sextant) { case 0: @@ -290,21 +289,31 @@ void hsl2rgb(double h,double s,double l, } +void TuneColorDialog::tabChanged(int current) +{ + // refresh image with the current settings when switching tabs + updateImage(m_hue,m_sat,m_gam); +} void TuneColorDialog::updateImage(int hue,int sat,int gam) { - QImage coloredImg(m_image->width(),m_image->height(),QImage::Format_RGB32); - uint *srcPixel = (uint *)m_image->scanLine(0); + QImage *image = m_tab->currentIndex()==0 ? &m_imageLight : &m_imageDark; + QLabel *label = m_tab->currentIndex()==0 ? m_imageLabLight : m_imageLabDark; + QImage coloredImg(image->width(),image->height(),QImage::Format_RGB32); + uint *srcPixel = (uint *)image->scanLine(0); uint *dstPixel = (uint *)coloredImg.scanLine(0); uint nrPixels = coloredImg.width()*coloredImg.height(); + double r,g,b; for (uint i=0;isetPixmap(QPixmap::fromImage(coloredImg)); + + QPixmap pm = QPixmap::fromImage(coloredImg); + pm.setDevicePixelRatio(2.0); + label->setPixmap(pm); m_hue = hue; m_sat = sat; m_gam = gam; @@ -533,7 +542,8 @@ Step1::Step1(Wizard *wizard,const QHash &modelData) : m_wizard(w //--------------------------------------------------- QFrame *f = new QFrame( this ); - f->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + f->setFrameShape(QFrame::HLine); + f->setFrameShadow(QFrame::Sunken); layout->addWidget(f); l = new QLabel(this); @@ -559,7 +569,8 @@ Step1::Step1(Wizard *wizard,const QHash &modelData) : m_wizard(w //--------------------------------------------------- f = new QFrame( this ); - f->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + f->setFrameShape(QFrame::HLine); + f->setFrameShadow(QFrame::Sunken); layout->addWidget(f); l = new QLabel(this); @@ -778,7 +789,8 @@ Step2::Step2(Wizard *wizard,const QHash &modelData) //--------------------------------------------------- QFrame *f = new QFrame( this ); - f->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + f->setFrameShape(QFrame::HLine); + f->setFrameShadow(QFrame::Sunken); layout->addWidget(f); m_optimizeLangGroup = new QButtonGroup(this); diff --git a/addon/doxywizard/wizard.h b/addon/doxywizard/wizard.h index 81eeb359b7b..8cee09f4b1e 100644 --- a/addon/doxywizard/wizard.h +++ b/addon/doxywizard/wizard.h @@ -1,10 +1,10 @@ /****************************************************************************** * - * Copyright (C) 1997-2019 by Dimitri van Heesch. + * Copyright (C) 1997-2025 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its - * documentation under the terms of the GNU General Public License is hereby - * granted. No representations are made about the suitability of this software + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * @@ -16,6 +16,7 @@ #include #include #include +#include class Input; class QTreeWidget; @@ -27,8 +28,8 @@ class QPushButton; class QRadioButton; class QGroupBox; class QButtonGroup; +class QTabWidget; class Wizard; -class QImage; class QLabel; class TuneColorDialog : public QDialog @@ -43,10 +44,14 @@ class TuneColorDialog : public QDialog private slots: void updateImage(int hue,int sat,int val); + void tabChanged(int); private: - QImage *m_image = nullptr; - QLabel *m_imageLab = nullptr; + QImage m_imageLight; + QImage m_imageDark; + QLabel *m_imageLabLight = nullptr; + QLabel *m_imageLabDark = nullptr; + QTabWidget *m_tab = nullptr; int m_hue = 0; int m_sat = 0; int m_gam = 0; @@ -102,8 +107,8 @@ class Step1 : public QWidget void init(); private slots: - void selectSourceDir(); - void selectDestinationDir(); + void selectSourceDir(); + void selectDestinationDir(); void selectProjectIcon(); void setProjectName(const QString &name); void setProjectBrief(const QString &desc); diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 96e0cde2aab..8fd790ed733 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -60,6 +60,7 @@ function(set_project_warnings project_name) -Wdouble-promotion # warn if float is implicit promoted to double -Wformat=2 # warn on security issues around functions that format output # (ie printf) + $<$:-Wcomma> # turn off warning caused by generated code (flex) -Wno-unused-parameter @@ -95,6 +96,7 @@ function(set_project_warnings project_name) -Wdouble-promotion # warn if float is implicit promoted to double -Wformat=2 # warn on security issues around functions that format output # (ie printf) + $<$:-Wextra-semi> # turn off warning caused by generated code (flex) -Wno-unused-parameter diff --git a/cmake/WindowsEncoding.cmake b/cmake/WindowsEncoding.cmake index 8f31bf04d3e..c3bc824ba70 100644 --- a/cmake/WindowsEncoding.cmake +++ b/cmake/WindowsEncoding.cmake @@ -1,8 +1,8 @@ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") include(FindPythonInterp) execute_process( - COMMAND ${Python_EXECUTABLE} "${CMAKE_SOURCE_DIR}/cmake/QueryCodePage.py" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/QueryCodePage.py" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE ReturnCode OUTPUT_VARIABLE CodePage ) diff --git a/cmake/version.cmake b/cmake/version.cmake index 9fbc6a9c5ff..c5f17126008 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -1,3 +1,3 @@ -file (STRINGS "${TOP}/VERSION" VERSION) +file (STRINGS "${PROJECT_SOURCE_DIR}/VERSION" VERSION) set(ENV{VERSION} "${VERSION}") -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${TOP}/VERSION) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/VERSION) diff --git a/deps/TinyDeflate/gunzip.hh b/deps/TinyDeflate/gunzip.hh index 652058ab823..b1f79711800 100644 --- a/deps/TinyDeflate/gunzip.hh +++ b/deps/TinyDeflate/gunzip.hh @@ -702,7 +702,7 @@ namespace gunzip_ns // Note: Throws away progress already made traversing the tree return ~std::uint_least32_t(0); // error flag } - cur = (unsigned(cur) << 1) | unsigned(bool(p)); + cur = (unsigned(cur) << 1) | bool(p); #ifdef DEFL_DO_HUFF_STATS if(len > maxlen) { @@ -944,27 +944,23 @@ namespace gunzip_ns // The following routines are macros rather than e.g. lambda functions, // in order to make them inlined in the function structure, and breakable/resumable. - #define CONCAT(a, b) a##b // Bit-by-bit input routine - #define DummyGetBits_(line,numbits) do { \ - auto CONCAT(pd,line) = state.template GetBits(std::forward(input), numbits); \ - if((Abortable & Flag_InputAbortable) && !~CONCAT(pd,line)) return -2; \ + #define DummyGetBits(numbits) do { \ + auto p = state.template GetBits(std::forward(input), numbits); \ + if((Abortable & Flag_InputAbortable) && !~p) return -2; \ } while(0) - #define DummyGetBits(numbits) DummyGetBits_(__LINE__, numbits) - #define GetBits_(line,numbits, target) \ - auto CONCAT(pb,line) = state.template GetBits(std::forward(input), numbits); \ - if((Abortable & Flag_InputAbortable) && !~CONCAT(pb,line)) return -2; \ - target = CONCAT(pb,line) - #define GetBits(numbits, target) GetBits_(__LINE__, numbits, target) + #define GetBits(numbits, target) \ + auto p = state.template GetBits(std::forward(input), numbits); \ + if((Abortable & Flag_InputAbortable) && !~p) return -2; \ + target = p // Huffman tree read routine. - #define HuffRead_(line, tree, target) \ - auto CONCAT(ph,line) = state.template HuffRead(std::forward(input), tree); \ - if((Abortable & Flag_InputAbortable) && !~CONCAT(ph,line)) return -2; \ - target = CONCAT(ph,line) - #define HuffRead(tree, target) HuffRead_(__LINE__, tree, target) + #define HuffRead(tree, target) \ + auto p = state.template HuffRead(std::forward(input), tree); \ + if((Abortable & Flag_InputAbortable) && !~p) return -2; \ + target = p #define Fail_If(condition) do { \ /*assert(!(condition));*/ \ @@ -1141,21 +1137,21 @@ namespace gunzip_ns //fprintf(stderr, "both track flag\n"); SizeTracker tracker; return tracker(Gunzip - (tracker.template ForwardInput(i), tracker.template ForwardOutput(o), tracker.template ForwardWindow(c), std::forward(b))); + (tracker.template ForwardInput(i), tracker.template ForwardOutput(o), tracker.template ForwardWindow(c), std::forward(b))); } else if constexpr(code & Flag_TrackIn) { //fprintf(stderr, "in track flag\n"); SizeTracker tracker; return tracker(Gunzip - (tracker.template ForwardInput(i),std::forward(o),std::forward(c),std::forward(b))); + (tracker.template ForwardInput(i),std::forward(o),std::forward(c),std::forward(b))); } else if constexpr(code & Flag_TrackOut) { //fprintf(stderr, "out track flag\n"); SizeTracker tracker; return tracker(Gunzip - (std::forward(i), tracker.template ForwardOutput(o), tracker.template ForwardWindow(c), std::forward(b))); + (std::forward(i), tracker.template ForwardOutput(o), tracker.template ForwardWindow(c), std::forward(b))); } else { diff --git a/deps/fmt/README.md b/deps/fmt/README.md new file mode 100644 index 00000000000..39727183cb7 --- /dev/null +++ b/deps/fmt/README.md @@ -0,0 +1,2 @@ +The include dir contains a copy of the files in deps/spdlog/include/spdlog/fmt/bundled +which is based on fmt version 11.2.0 see https://github.com/fmtlib/fmt/releases diff --git a/deps/fmt/include/fmt/args.h b/deps/fmt/include/fmt/args.h new file mode 100644 index 00000000000..3ff47880748 --- /dev/null +++ b/deps/fmt/include/fmt/args.h @@ -0,0 +1,220 @@ +// Formatting library for C++ - dynamic argument lists +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#ifndef FMT_MODULE +# include // std::reference_wrapper +# include // std::unique_ptr +# include +#endif + +#include "format.h" // std_string_view + +FMT_BEGIN_NAMESPACE +namespace detail { + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template auto unwrap(const T& v) -> const T& { return v; } +template +auto unwrap(const std::reference_wrapper& v) -> const T& { + return static_cast(v); +} + +// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC +// 2022 (v17.10.0). +// +// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for +// templates it doesn't complain about inability to deduce single translation +// unit for placing vtable. So node is made a fake template. +template struct node { + virtual ~node() = default; + std::unique_ptr> next; +}; + +class dynamic_arg_list { + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template auto push(const Arg& arg) -> const T& { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +/** + * A dynamic list of formatting arguments with storage. + * + * It can be implicitly converted into `fmt::basic_format_args` for passing + * into type-erased formatting functions such as `fmt::vformat`. + */ +template class dynamic_format_arg_store { + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_t = conditional_t< + std::is_convertible>::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + auto data() const -> const basic_format_arg* { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(arg); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) + data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); + data_.emplace_back(detail::unwrap(arg.value)); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0] = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + constexpr dynamic_format_arg_store() = default; + + operator basic_format_args() const { + return basic_format_args(data(), static_cast(data_.size()), + !named_info_.empty()); + } + + /** + * Adds an argument into the dynamic store for later passing to a formatting + * function. + * + * Note that custom types and string types (but not string views) are copied + * into the store dynamically allocating memory if necessary. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * store.push_back(42); + * store.push_back("abc"); + * store.push_back(1.5f); + * std::string result = fmt::vformat("{} and {} and {}", store); + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + * Adds a reference to the argument into the dynamic store for later passing + * to a formatting function. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * char band[] = "Rolling Stones"; + * store.push_back(std::cref(band)); + * band[9] = 'c'; // Changing str affects the output. + * std::string result = fmt::vformat("{}", store); + * // result == "Rolling Scones" + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + * Adds named argument into the dynamic store for later passing to a + * formatting function. `std::reference_wrapper` is supported to avoid + * copying of the argument. The name is always copied into the store. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /// Erase all elements from the store. + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = {}; + } + + /// Reserves space to store at least `new_cap` arguments including + /// `new_cap_named` named arguments. + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } + + /// Returns the number of elements in the store. + size_t size() const noexcept { return data_.size(); } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/deps/fmt/include/fmt/base.h b/deps/fmt/include/fmt/base.h new file mode 100644 index 00000000000..87b3fd7cb48 --- /dev/null +++ b/deps/fmt/include/fmt/base.h @@ -0,0 +1,2989 @@ +// Formatting library for C++ - the base API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_BASE_H_ +#define FMT_BASE_H_ + +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // memcmp + +# include // std::enable_if +#endif + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 110200 + +// Detect compiler versions. +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif +#if defined(__ICL) +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif +#if defined(_MSC_VER) +# define FMT_MSC_VERSION _MSC_VER +#else +# define FMT_MSC_VERSION 0 +#endif + +// Detect standard library versions. +#ifdef _GLIBCXX_RELEASE +# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE +#else +# define FMT_GLIBCXX_RELEASE 0 +#endif +#ifdef _LIBCPP_VERSION +# define FMT_LIBCPP_VERSION _LIBCPP_VERSION +#else +# define FMT_LIBCPP_VERSION 0 +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +// Detect __has_*. +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif +#ifdef __has_include +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(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 + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Detect C++14 relaxed constexpr. +#ifdef FMT_USE_CONSTEXPR +// Use the provided definition. +#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L +// GCC only allows constexpr member functions in non-literal types since 7.2: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. +# define FMT_USE_CONSTEXPR 1 +#elif FMT_ICC_VERSION +# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 +#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 +# define FMT_USE_CONSTEXPR 1 +#else +# define FMT_USE_CONSTEXPR 0 +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. +#if !defined(__cpp_lib_is_constant_evaluated) +# define FMT_USE_CONSTEVAL 0 +#elif FMT_CPLUSPLUS < 201709L +# define FMT_USE_CONSTEVAL 0 +#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 +# define FMT_USE_CONSTEVAL 0 +#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 +# define FMT_USE_CONSTEVAL 0 +#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L +# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. +#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929 +# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10. +#elif defined(__cpp_consteval) +# define FMT_USE_CONSTEVAL 1 +#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 +# define FMT_USE_CONSTEVAL 1 +#else +# define FMT_USE_CONSTEVAL 0 +#endif +#if FMT_USE_CONSTEVAL +# define FMT_CONSTEVAL consteval +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEVAL +# define FMT_CONSTEXPR20 +#endif + +// Check if exceptions are disabled. +#ifdef FMT_USE_EXCEPTIONS +// Use the provided definition. +#elif defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_USE_EXCEPTIONS 0 +#elif defined(__clang__) && !defined(__cpp_exceptions) +# define FMT_USE_EXCEPTIONS 0 +#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS +# define FMT_USE_EXCEPTIONS 0 +#else +# define FMT_USE_EXCEPTIONS 1 +#endif +#if FMT_USE_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 FMT_NO_UNIQUE_ADDRESS +// Use the provided definition. +#elif FMT_CPLUSPLUS < 202002L +// Not supported. +#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) +# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). +#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION +# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#endif +#ifndef FMT_NO_UNIQUE_ADDRESS +# define FMT_NO_UNIQUE_ADDRESS +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifdef FMT_NODISCARD +// Use the provided definition. +#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +#else +# define FMT_NODISCARD +#endif + +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +#else +# define FMT_VISIBILITY(value) +#endif + +// Detect pragmas. +#define FMT_PRAGMA_IMPL(x) _Pragma(#x) +#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) +// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 +// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. +# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) +#else +# define FMT_PRAGMA_GCC(x) +#endif +#if FMT_CLANG_VERSION +# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) +#else +# define FMT_PRAGMA_CLANG(x) +#endif +#if FMT_MSC_VERSION +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_WARNING(...) +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_PRAGMA_GCC(push_options) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +FMT_PRAGMA_GCC(optimize("Og")) +# define FMT_GCC_OPTIMIZED +#endif +FMT_PRAGMA_CLANG(diagnostic push) + +#ifdef FMT_ALWAYS_INLINE +// Use the provided definition. +#elif FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE inline +#endif +// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. +#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED) +# define FMT_INLINE FMT_ALWAYS_INLINE +#else +# define FMT_INLINE inline +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v11 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_EXPORT +# define FMT_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT +#endif + +#ifdef _WIN32 +# define FMT_WIN32 1 +#else +# define FMT_WIN32 0 +#endif + +#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 +# if defined(FMT_LIB_EXPORT) +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_OPTIMIZE_SIZE +# define FMT_OPTIMIZE_SIZE 0 +#endif + +// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher +// per-call binary size by passing built-in types through the extension API. +#ifndef FMT_BUILTIN_TYPES +# define FMT_BUILTIN_TYPES 1 +#endif + +#define FMT_APPLY_VARIADIC(expr) \ + using unused = int[]; \ + (void)unused { 0, (expr, 0)... } + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older 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; +template +using make_unsigned_t = typename std::make_unsigned::type; +template +using underlying_t = typename std::underlying_type::type; +template using decay_t = typename std::decay::type; +using nullptr_t = decltype(nullptr); + +#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION +// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. +template struct void_t_impl { + using type = void; +}; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif + +struct monostate { + constexpr 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). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +template constexpr auto min_of(T a, T b) -> T { + return a < b ? a : b; +} +template constexpr auto max_of(T a, T b) -> T { + return a > b ? a : b; +} + +namespace detail { +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr auto is_constant_evaluated(bool default_value = false) noexcept + -> bool { +// Workaround for incompatibility between clang 14 and libstdc++ consteval-based +// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. +#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { + return val; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#if defined(FMT_ASSERT) +// Use the provided definition. +#elif defined(NDEBUG) +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + fmt::detail::ignore_unused((condition), (message)) +#else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +#endif + +#ifdef FMT_USE_INT128 +// Use the provided definition. +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +inline auto map(int128_opt x) -> int128_opt { return x; } +inline auto map(uint128_opt x) -> uint128_opt { return x; } +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +inline auto map(int128_opt) -> monostate { return {}; } +inline auto map(uint128_opt) -> monostate { return {}; } +#endif + +#ifndef FMT_USE_BITINT +# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) +#endif + +#if FMT_USE_BITINT +FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") +template using bitint = _BitInt(N); +template using ubitint = unsigned _BitInt(N); +#else +template struct bitint {}; +template struct ubitint {}; +#endif // FMT_USE_BITINT + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); + return static_cast>(value); +} + +template +using unsigned_char = conditional_t; + +// A heuristic to detect std::string and std::[experimental::]string_view. +// It is mainly used to avoid dependency on <[experimental/]string_view>. +template +struct is_std_string_like : std::false_type {}; +template +struct is_std_string_like().find_first_of( + typename T::value_type(), 0))>> + : std::is_convertible().data()), + const typename T::value_type*> {}; + +// Check if the literal encoding is UTF-8. +enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; +enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; + +#ifndef FMT_UNICODE +# define FMT_UNICODE 1 +#endif + +static_assert(!FMT_UNICODE || use_utf8, + "Unicode support requires compiling with /utf-8"); + +template constexpr const char* narrow(const T*) { return nullptr; } +constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } + +template +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) + -> int { + if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); + for (; n != 0; ++s1, ++s2, --n) { + if (*s1 < *s2) return -1; + if (*s1 > *s2) return 1; + } + return 0; +} + +namespace adl { +using namespace std; + +template +auto invoke_back_inserter() + -> decltype(back_inserter(std::declval())); +} // namespace adl + +template +struct is_back_insert_iterator : std::false_type {}; + +template +struct is_back_insert_iterator< + It, bool_constant()), + It>::value>> : std::true_type {}; + +// Extracts a reference to the container from *insert_iterator. +template +inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> + typename OutputIt::container_type& { + struct accessor : OutputIt { + FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {} + using OutputIt::container; + }; + return *accessor(it).container; +} +} // namespace detail + +// Parsing-related public API and forward declarations. +FMT_BEGIN_EXPORT + +/** + * 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::basic_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 value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + + /// Constructs a string view object from a C string and a size. + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} + + constexpr basic_string_view(nullptr_t) = delete; + + /// Constructs a string view object from a C string. +#if FMT_GCC_VERSION + FMT_ALWAYS_INLINE +#endif + FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { +#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION + if (std::is_same::value && !detail::is_constant_evaluated()) { + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. + return; + } +#endif + size_t len = 0; + while (*s++) ++len; + size_ = len; + } + + /// Constructs a string view from a `std::basic_string` or a + /// `std::basic_string_view` object. + template ::value&& std::is_same< + typename S::value_type, Char>::value)> + FMT_CONSTEXPR basic_string_view(const S& s) noexcept + : data_(s.data()), size_(s.size()) {} + + /// Returns a pointer to the string data. + constexpr auto data() const noexcept -> const Char* { return data_; } + + /// Returns the string size. + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept + -> bool { + return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { + return size_ >= 1 && *data_ == c; + } + FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { + return starts_with(basic_string_view(s)); + } + + FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { + int result = + detail::compare(data_, other.data_, min_of(size_, other.size_)); + if (result != 0) return result; + return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + } + + FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, + basic_string_view rhs) -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +// DEPRECATED! Will be merged with is_char and moved to detail. +template struct is_xchar : std::false_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_xchar : std::true_type {}; +#endif + +// Specifies if `T` is a character (code unit) type. +template struct is_char : is_xchar {}; +template <> struct is_char : std::true_type {}; + +template class basic_appender; +using appender = basic_appender; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; + +class context; +template class generic_context; +template class parse_context; + +// Longer aliases for C++20 compatibility. +template using basic_format_parse_context = parse_context; +using format_parse_context = parse_context; +template +using basic_format_context = + conditional_t::value, context, + generic_context>; +using format_context = context; + +template +using buffered_context = + conditional_t::value, context, + generic_context, Char>>; + +template class basic_format_arg; +template class basic_format_args; + +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +/// Reports a format error at compile time or, via a `format_error` exception, +/// at runtime. +// This function is intentionally not constexpr to give a compile-time error. +FMT_NORETURN FMT_API void report_error(const char* message); + +enum class presentation_type : unsigned char { + // Common specifiers: + none = 0, + debug = 1, // '?' + string = 2, // 's' (string, bool) + + // Integral, bool and character specifiers: + dec = 3, // 'd' + hex, // 'x' or 'X' + oct, // 'o' + bin, // 'b' or 'B' + chr, // 'c' + + // String and pointer specifiers: + pointer = 3, // 'p' + + // Floating-point specifiers: + exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) + fixed, // 'f' or 'F' + general, // 'g' or 'G' + hexfloat // 'a' or 'A' +}; + +enum class align { none, left, right, center, numeric }; +enum class sign { none, minus, plus, space }; +enum class arg_id_kind { none, index, name }; + +// Basic format specifiers for built-in and string types. +class basic_specs { + private: + // Data is arranged as follows: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |type |align| w | p | s |u|#|L| f | unused | + // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ + // + // w - dynamic width info + // p - dynamic precision info + // s - sign + // u - uppercase (e.g. 'X' for 'x') + // # - alternate form ('#') + // L - localized + // f - fill size + // + // Bitfields are not used because of compiler bugs such as gcc bug 61414. + enum : unsigned { + type_mask = 0x00007, + align_mask = 0x00038, + width_mask = 0x000C0, + precision_mask = 0x00300, + sign_mask = 0x00C00, + uppercase_mask = 0x01000, + alternate_mask = 0x02000, + localized_mask = 0x04000, + fill_size_mask = 0x38000, + + align_shift = 3, + width_shift = 6, + precision_shift = 8, + sign_shift = 10, + fill_size_shift = 15, + + max_fill_size = 4 + }; + + unsigned data_ = 1 << fill_size_shift; + static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); + + // Character (code unit) type is erased to prevent template bloat. + char fill_data_[max_fill_size] = {' '}; + + FMT_CONSTEXPR void set_fill_size(size_t size) { + data_ = (data_ & ~fill_size_mask) | + (static_cast(size) << fill_size_shift); + } + + public: + constexpr auto type() const -> presentation_type { + return static_cast(data_ & type_mask); + } + FMT_CONSTEXPR void set_type(presentation_type t) { + data_ = (data_ & ~type_mask) | static_cast(t); + } + + constexpr auto align() const -> align { + return static_cast((data_ & align_mask) >> align_shift); + } + FMT_CONSTEXPR void set_align(fmt::align a) { + data_ = (data_ & ~align_mask) | (static_cast(a) << align_shift); + } + + constexpr auto dynamic_width() const -> arg_id_kind { + return static_cast((data_ & width_mask) >> width_shift); + } + FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { + data_ = (data_ & ~width_mask) | (static_cast(w) << width_shift); + } + + FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { + return static_cast((data_ & precision_mask) >> + precision_shift); + } + FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { + data_ = (data_ & ~precision_mask) | + (static_cast(p) << precision_shift); + } + + constexpr bool dynamic() const { + return (data_ & (width_mask | precision_mask)) != 0; + } + + constexpr auto sign() const -> sign { + return static_cast((data_ & sign_mask) >> sign_shift); + } + FMT_CONSTEXPR void set_sign(fmt::sign s) { + data_ = (data_ & ~sign_mask) | (static_cast(s) << sign_shift); + } + + constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } + FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } + + constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } + FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } + FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } + + constexpr auto localized() const -> bool { + return (data_ & localized_mask) != 0; + } + FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } + + constexpr auto fill_size() const -> size_t { + return (data_ & fill_size_mask) >> fill_size_shift; + } + + template ::value)> + constexpr auto fill() const -> const Char* { + return fill_data_; + } + template ::value)> + constexpr auto fill() const -> const Char* { + return nullptr; + } + + template constexpr auto fill_unit() const -> Char { + using uchar = unsigned char; + return static_cast(static_cast(fill_data_[0]) | + (static_cast(fill_data_[1]) << 8) | + (static_cast(fill_data_[2]) << 16)); + } + + FMT_CONSTEXPR void set_fill(char c) { + fill_data_[0] = c; + set_fill_size(1); + } + + template + FMT_CONSTEXPR void set_fill(basic_string_view s) { + auto size = s.size(); + set_fill_size(size); + if (size == 1) { + unsigned uchar = static_cast>(s[0]); + fill_data_[0] = static_cast(uchar); + fill_data_[1] = static_cast(uchar >> 8); + fill_data_[2] = static_cast(uchar >> 16); + return; + } + FMT_ASSERT(size <= max_fill_size, "invalid fill"); + for (size_t i = 0; i < size; ++i) + fill_data_[i & 3] = static_cast(s[i]); + } + + FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { + set_fill_size(specs.fill_size()); + for (size_t i = 0; i < max_fill_size; ++i) + fill_data_[i] = specs.fill_data_[i]; + } +}; + +// Format specifiers for built-in and string types. +struct format_specs : basic_specs { + int width; + int precision; + + constexpr format_specs() : width(0), precision(-1) {} +}; + +/** + * Parsing context consisting of a format string range being parsed and an + * argument counter for automatic indexing. + */ +template class parse_context { + private: + basic_string_view fmt_; + int next_arg_id_; + + enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; + + FMT_CONSTEXPR void do_check_arg_id(int arg_id); + + public: + using char_type = Char; + using iterator = const Char*; + + constexpr explicit parse_context(basic_string_view fmt, + int next_arg_id = 0) + : fmt_(fmt), next_arg_id_(next_arg_id) {} + + /// Returns an iterator to the beginning of the format string range being + /// parsed. + constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } + + /// Returns an iterator past the end of the format string range being parsed. + constexpr auto end() const noexcept -> iterator { return fmt_.end(); } + + /// Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator it) { + fmt_.remove_prefix(detail::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 auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + report_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /// Reports an error if using the automatic argument indexing; otherwise + /// switches to the manual indexing. + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + report_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + FMT_CONSTEXPR void check_arg_id(basic_string_view) { + next_arg_id_ = -1; + } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); +}; + +FMT_END_EXPORT + +namespace detail { + +// Constructs fmt::basic_string_view from types implicitly convertible +// to it, deducing Char. Explicitly convertible types such as the ones returned +// from FMT_STRING are intentionally excluded. +template ::value)> +constexpr auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template ::value)> +constexpr auto to_string_view(const T& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} + +template +struct has_to_string_view : std::false_type {}; +// detail:: is intentional since to_string_view is not an extension point. +template +struct has_to_string_view< + T, void_t()))>> + : std::true_type {}; + +/// String's character (code unit) type. detail:: is intentional to prevent ADL. +template ()))> +using char_t = typename V::value_type; + +enum class type { + none_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(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_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, 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); + +constexpr auto is_integral_type(type t) -> bool { + return t > type::none_type && t <= type::last_integer_type; +} +constexpr auto is_arithmetic_type(type t) -> bool { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +struct view {}; + +template +struct is_view : std::false_type {}; +template +struct is_view> : std::is_base_of {}; + +template struct named_arg; +template struct is_named_arg : std::false_type {}; +template struct is_static_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template struct named_arg : view { + const Char* name; + const T& value; + + named_arg(const Char* n, const T& v) : name(n), value(v) {} + static_assert(!is_named_arg::value, "nested named arguments"); +}; + +template constexpr auto count() -> int { return B ? 1 : 0; } +template constexpr auto count() -> int { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> int { + return count::value...>(); +} +template constexpr auto count_static_named_args() -> int { + return count::value...>(); +} + +template struct named_arg_info { + const Char* name; + int id; +}; + +// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +template +FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, + int named_arg_index, + basic_string_view arg_name) { + for (int i = 0; i < named_arg_index; ++i) { + if (named_args[i].name == arg_name) report_error("duplicate named arg"); + } +} + +template ::value)> +void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { + ++arg_index; +} +template ::value)> +void init_named_arg(named_arg_info* named_args, int& arg_index, + int& named_arg_index, const T& arg) { + check_for_duplicate(named_args, named_arg_index, arg.name); + named_args[named_arg_index++] = {arg.name, arg_index++}; +} + +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, + int&) { + ++arg_index; +} +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, + int& arg_index, int& named_arg_index) { + check_for_duplicate(named_args, named_arg_index, T::name); + named_args[named_arg_index++] = {T::name, arg_index++}; +} + +// 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) && FMT_BUILTIN_TYPES }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +template +using format_as_result = + remove_cvref_t()))>; +template +using format_as_member_result = + remove_cvref_t::format_as(std::declval()))>; + +template +struct use_format_as : std::false_type {}; +// format_as member is only used to avoid injection into the std namespace. +template +struct use_format_as_member : std::false_type {}; + +// Only map owning types because mapping views can be unsafe. +template +struct use_format_as< + T, bool_constant>::value>> + : std::true_type {}; +template +struct use_format_as_member< + T, bool_constant>::value>> + : std::true_type {}; + +template > +using use_formatter = + bool_constant<(std::is_class::value || std::is_enum::value || + std::is_union::value || std::is_array::value) && + !has_to_string_view::value && !is_named_arg::value && + !use_format_as::value && !use_format_as_member::value>; + +template > +auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) + -> decltype(formatter().format(*p, *ctx), std::true_type()); +template auto has_formatter_impl(...) -> std::false_type; + +// T can be const-qualified to check if it is const-formattable. +template constexpr auto has_formatter() -> bool { + return decltype(has_formatter_impl(static_cast(nullptr)))::value; +} + +// Maps formatting argument types to natively supported types or user-defined +// types with formatters. Returns void on errors to be SFINAE-friendly. +template struct type_mapper { + static auto map(signed char) -> int; + static auto map(unsigned char) -> unsigned; + static auto map(short) -> int; + static auto map(unsigned short) -> unsigned; + static auto map(int) -> int; + static auto map(unsigned) -> unsigned; + static auto map(long) -> long_type; + static auto map(unsigned long) -> ulong_type; + static auto map(long long) -> long long; + static auto map(unsigned long long) -> unsigned long long; + static auto map(int128_opt) -> int128_opt; + static auto map(uint128_opt) -> uint128_opt; + static auto map(bool) -> bool; + + template + static auto map(bitint) -> conditional_t; + template + static auto map(ubitint) + -> conditional_t; + + template ::value)> + static auto map(T) -> conditional_t< + std::is_same::value || std::is_same::value, Char, void>; + + static auto map(float) -> float; + static auto map(double) -> double; + static auto map(long double) -> long double; + + static auto map(Char*) -> const Char*; + static auto map(const Char*) -> const Char*; + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + static auto map(const T&) -> conditional_t::value, + basic_string_view, void>; + + static auto map(void*) -> const void*; + static auto map(const void*) -> const void*; + static auto map(volatile void*) -> const void*; + static auto map(const volatile void*) -> const void*; + static auto map(nullptr_t) -> const void*; + template ::value || + std::is_member_pointer::value)> + static auto map(const T&) -> void; + + template ::value)> + static auto map(const T& x) -> decltype(map(format_as(x))); + template ::value)> + static auto map(const T& x) -> decltype(map(formatter::format_as(x))); + + template ::value)> + static auto map(T&) -> conditional_t(), T&, void>; + + template ::value)> + static auto map(const T& named_arg) -> decltype(map(named_arg.value)); +}; + +// detail:: is used to workaround a bug in MSVC 2017. +template +using mapped_t = decltype(detail::type_mapper::map(std::declval())); + +// A type constant after applying type_mapper. +template +using mapped_type_constant = type_constant, Char>; + +template ::value> +using stored_type_constant = std::integral_constant< + type, Context::builtin_types || TYPE == type::int_type ? TYPE + : type::custom_type>; +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public parse_context { + private: + int num_args_; + const type* types_; + using base = parse_context; + + public: + FMT_CONSTEXPR explicit compile_parse_context(basic_string_view fmt, + int num_args, const type* types, + int next_arg_id = 0) + : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) report_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) report_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + ignore_unused(arg_id); + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + report_error("width/precision is not integer"); + } +}; + +// An argument reference. +template union arg_ref { + FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} + FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; +} + +// 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 auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) return static_cast(value); + // Check for overflow. + unsigned max = INT_MAX; + return num_digits == digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align { + switch (c) { + case '<': return align::left; + case '>': return align::right; + case '^': return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = parse_nonnegative_int(begin, end, INT_MAX); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + report_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { + report_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template struct dynamic_spec_handler { + parse_context& ctx; + arg_ref& ref; + arg_id_kind& kind; + + FMT_CONSTEXPR void on_index(int id) { + ref = id; + kind = arg_id_kind::index; + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = id; + kind = arg_id_kind::name; + ctx.check_arg_id(id); + } +}; + +template struct parse_dynamic_spec_result { + const Char* end; + arg_id_kind kind; +}; + +// Parses integer | "{" [arg_id] "}". +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + parse_context& ctx) + -> parse_dynamic_spec_result { + FMT_ASSERT(begin != end, ""); + auto kind = arg_id_kind::none; + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val == -1) report_error("number is too big"); + value = val; + } else { + if (*begin == '{') { + ++begin; + if (begin != end) { + Char c = *begin; + if (c == '}' || c == ':') { + int id = ctx.next_arg_id(); + ref = id; + kind = arg_id_kind::index; + ctx.check_dynamic_spec(id); + } else { + begin = parse_arg_id(begin, end, + dynamic_spec_handler{ctx, ref, kind}); + } + } + if (begin != end && *begin == '}') return {++begin, kind}; + } + report_error("invalid format string"); + } + return {begin, kind}; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + format_specs& specs, arg_ref& width_ref, + parse_context& ctx) -> const Char* { + auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + specs.set_dynamic_width(result.kind); + return result.end; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + format_specs& specs, + arg_ref& precision_ref, + parse_context& ctx) -> const Char* { + ++begin; + if (begin == end) { + report_error("invalid precision"); + return begin; + } + auto result = + parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); + specs.set_dynamic_precision(result.kind); + return result.end; +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, + dynamic_format_specs& specs, + parse_context& ctx, type arg_type) + -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + report_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { + if (!in(arg_type, set)) report_error("invalid format specifier"); + specs.set_type(pres_type); + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.set_align(parse_align(c)); + ++begin; + break; + case '+': + case ' ': + specs.set_sign(c == ' ' ? sign::space : sign::plus); + FMT_FALLTHROUGH; + case '-': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.set_alt(); + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + report_error("format specifier requires numeric argument"); + if (specs.align() == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.set_align(align::numeric); + specs.set_fill('0'); + } + ++begin; + break; + // clang-format off + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '{': + // clang-format on + enter_state(state::width); + begin = parse_width(begin, end, specs, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.set_localized(); + ++begin; + break; + case 'd': return parse_presentation_type(pres::dec, integral_set); + case 'X': specs.set_upper(); FMT_FALLTHROUGH; + case 'x': return parse_presentation_type(pres::hex, integral_set); + case 'o': return parse_presentation_type(pres::oct, integral_set); + case 'B': specs.set_upper(); FMT_FALLTHROUGH; + case 'b': return parse_presentation_type(pres::bin, integral_set); + case 'E': specs.set_upper(); FMT_FALLTHROUGH; + case 'e': return parse_presentation_type(pres::exp, float_set); + case 'F': specs.set_upper(); FMT_FALLTHROUGH; + case 'f': return parse_presentation_type(pres::fixed, float_set); + case 'G': specs.set_upper(); FMT_FALLTHROUGH; + case 'g': return parse_presentation_type(pres::general, float_set); + case 'A': specs.set_upper(); FMT_FALLTHROUGH; + case 'a': return parse_presentation_type(pres::hexfloat, float_set); + case 'c': + if (arg_type == type::bool_type) report_error("invalid format specifier"); + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + report_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + report_error("invalid fill character '{'"); + return begin; + } + auto alignment = parse_align(to_ascii(*fill_end)); + enter_state(state::align, alignment != align::none); + specs.set_fill( + basic_string_view(begin, to_unsigned(fill_end - begin))); + specs.set_align(alignment); + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); + } +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, + const Char* end, + Handler&& handler) + -> const Char* { + ++begin; + if (begin == end) { + handler.on_error("invalid format string"); + return end; + } + int arg_id = 0; + switch (*begin) { + case '}': + handler.on_replacement_field(handler.on_arg_id(), begin); + return begin + 1; + case '{': handler.on_text(begin, begin + 1); return begin + 1; + case ':': arg_id = handler.on_arg_id(); break; + default: { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + } adapter = {handler, 0}; + begin = parse_arg_id(begin, end, adapter); + arg_id = adapter.arg_id; + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(arg_id, begin); + return begin + 1; + } + if (c != ':') { + handler.on_error("missing '}' in format string"); + return end; + } + break; + } + } + begin = handler.on_format_specs(arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + return begin + 1; +} + +template +FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, + Handler&& handler) { + auto begin = fmt.data(), end = begin + fmt.size(); + auto p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); +} + +// Checks char specs and returns true iff the presentation type is char-like. +FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { + auto type = specs.type(); + if (type != presentation_type::none && type != presentation_type::chr && + type != presentation_type::debug) { + return false; + } + if (specs.align() == align::numeric || specs.sign() != sign::none || + specs.alt()) { + report_error("invalid format specifier for char"); + } + return true; +} + +// A base class for compile-time strings. +struct compile_string {}; + +template +FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). +FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { + using mapped_type = remove_cvref_t>; + constexpr bool formattable = + std::is_constructible>::value; + if (!formattable) return ctx.begin(); // Error is reported in the value ctor. + using formatted_type = conditional_t; + return formatter().parse(ctx); +} + +template struct arg_pack {}; + +template +class format_string_checker { + private: + type types_[max_of(1, NUM_ARGS)]; + named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; + compile_parse_context context_; + + using parse_func = auto (*)(parse_context&) -> const Char*; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + + public: + template + FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, + arg_pack) + : types_{mapped_type_constant::value...}, + named_args_{}, + context_(fmt, NUM_ARGS, types_), + parse_funcs_{&invoke_parse...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_static_named_arg(named_args_, arg_index, named_arg_index)); + ignore_unused(arg_index, named_arg_index); + } + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + context_.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + for (int i = 0; i < NUM_NAMED_ARGS; ++i) { + if (named_args_[i].name == id) return named_args_[i].id; + } + if (!DYNAMIC_NAMES) on_error("argument not found"); + return -1; + } + + FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { + on_format_specs(id, begin, begin); // Call parse() on empty specs. + } + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + context_.advance_to(begin); + if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); + + // If id is out of range, it means we do not know the type and cannot parse + // the format at compile time. Instead, skip over content until we finish + // the format spec, accounting for any nested replacements. + for (int bracket_count = 0; + begin != end && (bracket_count > 0 || *begin != '}'); ++begin) { + if (*begin == '{') + ++bracket_count; + else if (*begin == '}') + --bracket_count; + } + return begin; + } + + FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { + report_error(message); + } +}; + +/// A contiguous memory buffer with an optional growing ability. It is an +/// internal class and shouldn't be used directly, only via `memory_buffer`. +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + using grow_fun = void (*)(buffer& buf, size_t capacity); + grow_fun grow_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept + : size_(sz), capacity_(sz), grow_(grow) {} + + constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, + size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /// Sets the buffer data and capacity. + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /// Returns the size of this buffer. + constexpr auto size() const noexcept -> size_t { return size_; } + + /// Returns the capacity of this buffer. + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /// Returns a pointer to the buffer data (not null-terminated). + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /// Clears this buffer. + FMT_CONSTEXPR void clear() { size_ = 0; } + + // Tries resizing the buffer to contain `count` elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR void try_resize(size_t count) { + try_reserve(count); + size_ = min_of(count, capacity_); + } + + // Tries increasing the buffer capacity to `new_capacity`. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow_(*this, new_capacity); + } + + FMT_CONSTEXPR void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /// Appends data to the end of the buffer. + template +// Workaround for MSVC2019 to fix error C2893: Failed to specialize function +// template 'void fmt::v11::detail::buffer::append(const U *,const U *)'. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940 + FMT_CONSTEXPR20 +#endif + void + append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + // A loop is faster than memcpy on small sizes. + T* out = ptr_ + size_; + for (size_t i = 0; i < count; ++i) out[i] = begin[i]; + size_ += count; + begin += count; + } + } + + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + constexpr explicit buffer_traits(size_t) {} + constexpr auto count() const -> size_t { return 0; } + constexpr auto limit(size_t size) const -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + constexpr auto count() const -> size_t { return count_; } + FMT_CONSTEXPR auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return min_of(size, n); + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buffer_size) static_cast(buf).flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + const T* begin = data_; + const T* end = begin + this->limit(size); + while (begin != end) *out_++ = *begin++; + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : Traits(other), + buffer(grow, data_, 0, buffer_size), + out_(other.out_) {} + ~iterator_buffer() { + // Don't crash if flush fails during unwinding. + FMT_TRY { flush(); } + FMT_CATCH(...) {} + } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) + static_cast(buf).flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : fixed_buffer_traits(other), + buffer(static_cast(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer : public buffer { + public: + explicit iterator_buffer(T* out, size_t = 0) + : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +template +class container_buffer : public buffer { + private: + using value_type = typename Container::value_type; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { + auto& self = static_cast(buf); + self.container.resize(capacity); + self.set(&self.container[0], capacity); + } + + public: + Container& container; + + explicit container_buffer(Container& c) + : buffer(grow, c.size()), container(c) {} +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer< + OutputIt, + enable_if_t::value && + is_contiguous::value, + typename OutputIt::container_type::value_type>> + : public container_buffer { + private: + using base = container_buffer; + + public: + explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} + explicit iterator_buffer(OutputIt out, size_t = 0) + : base(get_container(out)) {} + + auto out() -> OutputIt { return OutputIt(this->container); } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() != buffer_size) return; + static_cast(buf).count_ += buf.size(); + buf.clear(); + } + + public: + FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} + + constexpr auto count() const noexcept -> size_t { + return count_ + this->size(); + } +}; + +template +struct is_back_insert_iterator> : std::true_type {}; + +template +struct has_back_insert_iterator_container_append : std::false_type {}; +template +struct has_back_insert_iterator_container_append< + OutputIt, InputIt, + void_t()) + .append(std::declval(), + std::declval()))>> : std::true_type {}; + +// An optimized version of std::copy with the output value type (T). +template ::value&& + has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + get_container(out).append(begin, end); + return out; +} + +template ::value && + !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + auto& c = get_container(out); + c.insert(c.end(), begin, end); + return out; +} + +template ::value)> +FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template +FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { + return copy(s.begin(), s.end(), out); +} + +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + +// Maps an output iterator to a buffer. +template ::value)> +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { + return get_container(out); +} + +template +auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { + return buf.out(); +} +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; +} + +// This type is intentionally undefined, only used for errors. +template struct type_is_unformattable_for; + +template struct string_value { + const Char* data; + size_t size; + auto str() const -> basic_string_view { return {data, size}; } +}; + +template struct custom_value { + using char_type = typename Context::char_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +struct custom_tag {}; + +#if !FMT_BUILTIN_TYPES +# define FMT_BUILTIN , monostate +#else +# define FMT_BUILTIN +#endif + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt 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; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(signed char x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(signed short x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(int x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} + FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} + FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN) + : value(ulong_type(x)) {} + constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} + constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN) + : ulong_long_value(x) {} + FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {} + FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {} + constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} + + template + constexpr FMT_INLINE value(bitint x FMT_BUILTIN) : long_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + template + constexpr FMT_INLINE value(ubitint x FMT_BUILTIN) : ulong_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + + template ::value)> + constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + } + + constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} + constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} + FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} + + FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { + static_assert(std::is_same::value, + "mixing character types is disallowed"); + auto sv = to_string_view(x); + string.data = sv.data(); + string.size = sv.size(); + } + FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(const volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(nullptr_t) : pointer(nullptr) {} + + template ::value || + std::is_member_pointer::value)> + value(const T&) { + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + static_assert(sizeof(T) == 0, + "formatting of non-void pointers is disallowed"); + } + + template ::value)> + value(const T& x) : value(format_as(x)) {} + template ::value)> + value(const T& x) : value(formatter::format_as(x)) {} + + template ::value)> + value(const T& named_arg) : value(named_arg.value) {} + + template ::value || !FMT_BUILTIN_TYPES)> + FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {} + + FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + private: + template ())> + FMT_CONSTEXPR value(T& x, custom_tag) { + using value_type = remove_const_t; + // T may overload operator& e.g. std::vector::reference in libc++. + if (!is_constant_evaluated()) { + custom.value = + const_cast(&reinterpret_cast(x)); + } else { + custom.value = nullptr; +#if defined(__cpp_if_constexpr) + if constexpr (std::is_same*>::value) + custom.value = const_cast(&x); +#endif + } + custom.format = format_custom>; + } + + template ())> + FMT_CONSTEXPR value(const T&, custom_tag) { + // Cannot format an argument; to make type T formattable provide a + // formatter specialization: https://fmt.dev/latest/api.html#udt. + type_is_unformattable_for _; + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom(void* arg, parse_context& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + // format must be const for compatibility with std::format and compilation. + const auto& cf = f; + ctx.advance_to(cf.format(*static_cast(arg), ctx)); + } +}; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +template +struct is_output_iterator : std::false_type {}; + +template <> struct is_output_iterator : std::true_type {}; + +template +struct is_output_iterator< + It, T, + enable_if_t&>()++), + T>::value>> : std::true_type {}; + +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to an std::locale to avoid a heavy include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template locale_ref(const Locale& loc); + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +constexpr auto make_descriptor() -> unsigned long long { + return NUM_ARGS <= max_packed_args ? encode_types() + : is_unpacked_bit | NUM_ARGS; +} + +template +using arg_t = conditional_t, + basic_format_arg>; + +template +struct named_arg_store { + // args_[0].named_args points to named_args to avoid bloating format_args. + arg_t args[1 + NUM_ARGS]; + named_arg_info named_args[NUM_NAMED_ARGS]; + + template + FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) + : args{{named_args, NUM_NAMED_ARGS}, values...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_named_arg(named_args, arg_index, named_arg_index, values)); + } + + named_arg_store(named_arg_store&& rhs) { + args[0] = {named_args, NUM_NAMED_ARGS}; + for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) + args[i] = rhs.args[i]; + for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) + named_args[i] = rhs.named_args[i]; + } + + named_arg_store(const named_arg_store& rhs) = delete; + named_arg_store& operator=(const named_arg_store& rhs) = delete; + named_arg_store& operator=(named_arg_store&& rhs) = delete; + operator const arg_t*() const { return args + 1; } +}; + +// An array of references to arguments. It can be implicitly converted to +// `basic_format_args` for passing into type-erased formatting functions +// such as `vformat`. It is a plain struct to reduce binary size in debug mode. +template +struct format_arg_store { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + using type = + conditional_t[max_of(1, NUM_ARGS)], + named_arg_store>; + type args; +}; + +// TYPE can be different from type_constant, e.g. for __float128. +template struct native_formatter { + private: + dynamic_format_specs specs_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); + if (const_check(TYPE == type::char_type)) check_char_specs(specs_); + return end; + } + + template + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.set_type(set ? presentation_type::debug : presentation_type::none); + } + + FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline") + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template +struct locking + : bool_constant::value == type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + +FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc = {}); + +#if FMT_WIN32 +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +#else // format_args is passed by reference since it is defined later. +inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} +#endif +} // namespace detail + +// The main public API. + +template +FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { + // Argument id is only checked at compile time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && use_constexpr_cast) { + auto ctx = static_cast*>(this); + if (arg_id >= ctx->num_args()) report_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { + using detail::compile_parse_context; + if (detail::is_constant_evaluated() && use_constexpr_cast) + static_cast*>(this)->check_dynamic_spec(arg_id); +} + +FMT_BEGIN_EXPORT + +// An output iterator that appends to a buffer. It is used instead of +// back_insert_iterator to reduce symbol sizes and avoid dependency. +template class basic_appender { + protected: + detail::buffer* container; + + public: + using container_type = detail::buffer; + + FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} + + FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { + container->push_back(c); + return *this; + } + FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } +}; + +// A formatting argument. Context is a template parameter for the compiled API +// where output can be unbuffered. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + friend class basic_format_args; + + using char_type = typename Context::char_type; + + public: + class handle { + private: + detail::custom_value custom_; + + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(parse_context& parse_ctx, Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + template + basic_format_arg(T&& val) + : value_(val), type_(detail::stored_type_constant::value) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + auto type() const -> detail::type { return type_; } + + /** + * 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`. + */ + template + FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { + using detail::map; + switch (type_) { + case detail::type::none_type: break; + case detail::type::int_type: return vis(value_.int_value); + case detail::type::uint_type: return vis(value_.uint_value); + case detail::type::long_long_type: return vis(value_.long_long_value); + case detail::type::ulong_long_type: return vis(value_.ulong_long_value); + case detail::type::int128_type: return vis(map(value_.int128_value)); + case detail::type::uint128_type: return vis(map(value_.uint128_value)); + case detail::type::bool_type: return vis(value_.bool_value); + case detail::type::char_type: return vis(value_.char_value); + case detail::type::float_type: return vis(value_.float_value); + case detail::type::double_type: return vis(value_.double_value); + case detail::type::long_double_type: return vis(value_.long_double_value); + case detail::type::cstring_type: return vis(value_.string.data); + case detail::type::string_type: return vis(value_.string.str()); + case detail::type::pointer_type: return vis(value_.pointer); + case detail::type::custom_type: return vis(handle(value_.custom)); + } + return vis(monostate()); + } + + auto format_custom(const char_type* parse_begin, + parse_context& parse_ctx, Context& ctx) + -> bool { + if (type_ != detail::type::custom_type) return false; + parse_ctx.advance_to(parse_begin); + value_.custom.format(value_.custom.value, parse_ctx, ctx); + return true; + } +}; + +/** + * A view of a collection of formatting arguments. To avoid lifetime issues it + * should only be used as a parameter type in type-erased functions such as + * `vformat`: + * + * void vlog(fmt::string_view fmt, fmt::format_args args); // OK + * fmt::format_args args = fmt::make_format_args(); // Dangling reference + */ +template class basic_format_args { + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since 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 detail::value* values_; + const basic_format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + constexpr auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + template + using store = + detail::format_arg_store; + + public: + using format_arg = basic_format_arg; + + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /// Constructs a `basic_format_args` object from `format_arg_store`. + template + constexpr FMT_ALWAYS_INLINE basic_format_args( + const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + values_(s.args) {} + + template detail::max_packed_args)> + constexpr basic_format_args(const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + args_(s.args) {} + + /// Constructs a `basic_format_args` object from a dynamic list of arguments. + constexpr basic_format_args(const format_arg* args, int count, + bool has_named = false) + : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | + (has_named ? +detail::has_named_args_bit : 0)), + args_(args) {} + + /// Returns the argument with the specified id. + FMT_CONSTEXPR auto get(int id) const -> format_arg { + auto arg = format_arg(); + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (static_cast(id) >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +// A formatting context. +class context { + private: + appender out_; + format_args args_; + FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + + public: + /// The character type for the output. + using char_type = char; + + using iterator = appender; + using format_arg = basic_format_arg; + using parse_context_type FMT_DEPRECATED = parse_context<>; + template using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + /// Constructs a `context` object. References to the arguments are stored + /// in the object so make sure they have appropriate lifetimes. + FMT_CONSTEXPR context(iterator out, format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + context(context&&) = default; + context(const context&) = delete; + void operator=(const context&) = delete; + + FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } + inline auto arg(string_view name) const -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(string_view name) const -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() const -> iterator { return out_; } + + // Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator) {} + + FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } +}; + +template struct runtime_format_string { + basic_string_view str; +}; + +/** + * Creates a runtime format string. + * + * **Example**: + * + * // Check format string at runtime instead of compile-time. + * fmt::print(fmt::runtime("{:d}"), "I am not a number"); + */ +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } + +/// A compile-time format string. Use `format_string` in the public API to +/// prevent type deduction. +template struct fstring { + private: + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + string_view str; + using t = fstring; + + // Reports a compile-time error if S is not a valid format string for T. + template + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { + using namespace detail; + static_assert(count<(is_view>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) { + auto sv = string_view(str); + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(sv, checker(sv, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { + FMT_CONSTEXPR auto sv = string_view(S()); + FMT_CONSTEXPR int unused = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(unused); + } + fstring(runtime_format_string<> fmt) : str(fmt.str) {} + + // Returning by reference generates better code in debug mode. + FMT_ALWAYS_INLINE operator const string_view&() const { return str; } + auto get() const -> string_view { return str; } +}; + +template using format_string = typename fstring::t; + +template +using is_formattable = bool_constant::value, int*, T>, Char>, + void>::value>; +#ifdef __cpp_concepts +template +concept formattable = is_formattable, Char>::value; +#endif + +template +using has_formatter FMT_DEPRECATED = std::is_constructible>; + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> + : detail::native_formatter::value> { +}; + +/** + * Constructs an object that stores references to arguments and can be + * implicitly converted to `format_args`. `Context` can be omitted in which case + * it defaults to `context`. See `arg` for lifetime considerations. + */ +// Take arguments by lvalue references to avoid some lifetime issues, e.g. +// auto args = make_format_args(std::string()); +template (), + unsigned long long DESC = detail::make_descriptor()> +constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) + -> detail::format_arg_store { + // Suppress warnings for pathological types convertible to detail::value. + FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion") + return {{args...}}; +} + +template +using vargs = + detail::format_arg_store(), + detail::make_descriptor()>; + +/** + * Returns a named argument to be used in a formatting function. + * It should only be used in a call to a formatting function. + * + * **Example**: + * + * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + return {name, arg}; +} + +/// Formats a string and writes the output to `out`. +template , + char>::value)> +auto vformat_to(OutputIt&& out, string_view fmt, format_args args) + -> remove_cvref_t { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); +} + +/** + * Formats `args` according to specifications in `fmt`, writes the result to + * the output iterator `out` and returns the iterator past the end of the output + * range. `format_to` does not append a terminating null character. + * + * **Example**: + * + * auto out = std::vector(); + * fmt::format_to(std::back_inserter(out), "{}", 42); + */ +template , + char>::value)> +FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) + -> remove_cvref_t { + return vformat_to(out, fmt.str, vargs{{args...}}); +} + +template struct format_to_n_result { + /// Iterator past the end of the output range. + OutputIt out; + /// Total (not truncated) output size. + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + * Formats `args` according to specifications in `fmt`, writes up to `n` + * characters of the result to the output iterator `out` and returns the total + * (not truncated) output size and the iterator past the end of the output + * range. `format_to_n` does not append a terminating null character. + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt.str, vargs{{args...}}); +} + +struct format_to_result { + /// Pointer to just after the last successful write in the array. + char* out; + /// Specifies if the output was truncated. + bool truncated; + + FMT_CONSTEXPR operator char*() const { + // Report truncation to prevent silent data loss. + if (truncated) report_error("output is truncated"); + return out; + } +}; + +template +auto vformat_to(char (&out)[N], string_view fmt, format_args args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt, args); + return {result.out, result.size > N}; +} + +template +FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); + return {result.out, result.size > N}; +} + +/// Returns the number of chars in the output of `format(fmt, args...)`. +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(FILE* f, string_view fmt, format_args args); +FMT_API void vprintln(FILE* f, string_view fmt, format_args args); +FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::print("The answer is {}.", 42); + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(stdout, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) + : vprint(fmt.str, va); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the + * output to the file `f`. + * + * **Example**: + * + * fmt::print(stderr, "Don't {}!", "panic"); + */ +template +FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(f, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(f, fmt.str, va) + : vprint(f, fmt.str, va); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to the file `f` followed by a newline. +template +FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + return detail::const_check(detail::use_utf8) + ? vprintln(f, fmt.str, va) + : detail::vprint_mojibake(f, fmt.str, va, true); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to `stdout` followed by a newline. +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, static_cast(args)...); +} + +FMT_END_EXPORT +FMT_PRAGMA_CLANG(diagnostic pop) +FMT_PRAGMA_GCC(pop_options) +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_BASE_H_ diff --git a/deps/fmt/include/fmt/chrono.h b/deps/fmt/include/fmt/chrono.h new file mode 100644 index 00000000000..e0c81589ea1 --- /dev/null +++ b/deps/fmt/include/fmt/chrono.h @@ -0,0 +1,2330 @@ +// 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_ + +#ifndef FMT_MODULE +# include +# include +# include // std::isfinite +# include // std::memcpy +# include +# include +# include +# include +# include +#endif + +#include "format.h" + +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 auto lossless_integral_conversion(const From from, int& ec) + -> To { + 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 (detail::const_check(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 auto lossless_integral_conversion(const From from, int& ec) + -> To { + 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 (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (detail::const_check(F::digits > T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { + 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 auto safe_float_conversion(const From from, int& ec) -> To { + 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 auto safe_float_conversion(const From from, int& ec) -> To { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/// Safe duration_cast between floating point durations +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { + 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 (detail::const_check(Factor::num != 1)) { + constexpr auto max1 = detail::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 (detail::const_check(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 + +namespace detail { + +// Check if std::chrono::utc_time is available. +#ifdef FMT_USE_UTC_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_UTC_TIME 0 +#endif +#if FMT_USE_UTC_TIME +using utc_clock = std::chrono::utc_clock; +#else +struct utc_clock { + template void to_sys(T); +}; +#endif + +// Check if std::chrono::local_time is available. +#ifdef FMT_USE_LOCAL_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_LOCAL_TIME 0 +#endif +#if FMT_USE_LOCAL_TIME +using local_t = std::chrono::local_t; +#else +struct local_t {}; +#endif + +} // namespace detail + +template +using sys_time = std::chrono::time_point; + +template +using utc_time = std::chrono::time_point; + +template +using local_time = std::chrono::time_point; + +namespace detail { + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +template struct null {}; +inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } +inline auto localtime_s(...) -> null<> { return null<>(); } +inline auto gmtime_r(...) -> null<> { return null<>(); } +inline auto gmtime_s(...) -> null<> { return null<>(); } + +// It is defined here and not in ostream.h because the latter has expensive +// includes. +template class formatbuf : public StreamBuf { + private: + using char_type = typename StreamBuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename StreamBuf::int_type; + using traits_type = typename StreamBuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +inline auto get_classic_locale() -> const std::locale& { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; + +template +void write_codecvt(codecvt_result& out, string_view in, + const std::locale& loc) { + FMT_PRAGMA_CLANG(diagnostic push) + FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") + auto& f = std::use_facet>(loc); + FMT_PRAGMA_CLANG(diagnostic pop) + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), + std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (const_check(detail::use_utf8) && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && \ + (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto u = + to_utf8>(); + if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) + FMT_THROW(format_error("failed to format time")); + return copy(u.c_str(), u.c_str() + u.size(), out); + } + return copy(in.data(), in.data() + in.size(), out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template +inline void do_write(buffer& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf>(buf); + auto&& os = std::basic_ostream(&format_buf); + os.imbue(loc); + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return get_iterator(buf, out); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); +} + +template +using is_similar_arithmetic_type = + bool_constant<(std::is_integral::value && std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)>; + +FMT_NORETURN inline void throw_duration_error() { + FMT_THROW(format_error("cannot format duration")); +} + +// Cast one integral duration to another with an overflow check. +template ::value&& + std::is_integral::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if !FMT_SAFE_DURATION_CAST + return std::chrono::duration_cast(from); +#else + // The conversion factor: to.count() == factor * from.count(). + using factor = std::ratio_divide; + + using common_rep = typename std::common_type::type; + + int ec = 0; + auto count = safe_duration_cast::lossless_integral_conversion( + from.count(), ec); + if (ec) throw_duration_error(); + + // Multiply from.count() by factor and check for overflow. + if (const_check(factor::num != 1)) { + if (count > max_value() / factor::num) throw_duration_error(); + const auto min = (std::numeric_limits::min)() / factor::num; + if (const_check(!std::is_unsigned::value) && count < min) + throw_duration_error(); + count *= factor::num; + } + if (const_check(factor::den != 1)) count /= factor::den; + auto to = + To(safe_duration_cast::lossless_integral_conversion( + count, ec)); + if (ec) throw_duration_error(); + return to; +#endif +} + +template ::value&& + std::is_floating_point::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if FMT_SAFE_DURATION_CAST + // Throwing version of safe_duration_cast is only available for + // integer to integer or float to float casts. + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) throw_duration_error(); + return to; +#else + // Standard duration cast, may overflow. + return std::chrono::duration_cast(from); +#endif +} + +template ::value)> +auto duration_cast(std::chrono::duration from) -> To { + // Mixed integer <-> float cast is not supported by safe_duration_cast. + return std::chrono::duration_cast(from); +} + +template +auto to_time_t(sys_time time_point) -> std::time_t { + // Cannot use std::chrono::system_clock::to_time_t since this would first + // require a cast to std::chrono::system_clock::time_point, which could + // overflow. + return detail::duration_cast>( + time_point.time_since_epoch()) + .count(); +} + +namespace tz { + +// DEPRECATED! +struct time_zone { + template + auto to_sys(LocalTime) -> sys_time { + return {}; + } +}; +template auto current_zone(T...) -> time_zone* { + return nullptr; +} + +template void _tzset(T...) {} +} // namespace tz + +// DEPRECATED! +inline void tzset_once() { + static bool init = []() { + using namespace tz; + _tzset(); + return false; + }(); + ignore_unused(init); +} +} // namespace detail + +FMT_BEGIN_EXPORT + +/** + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in local time. Unlike `std::localtime`, this function is + * thread-safe on most platforms. + */ +FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + inline dispatcher(std::time_t t) : time_(t) {} + + inline auto run() -> bool { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } + + inline auto handle(detail::null<>) -> bool { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + inline auto fallback(int res) -> bool { return res == 0; } + +#if !FMT_MSC_VERSION + inline auto fallback(detail::null<>) -> bool { + using namespace fmt::detail; + 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_; +} + +#if FMT_USE_LOCAL_TIME +template +FMT_DEPRECATED auto localtime(std::chrono::local_time time) + -> std::tm { + using namespace std::chrono; + using namespace detail::tz; + return localtime(detail::to_time_t(current_zone()->to_sys(time))); +} +#endif + +/** + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this + * function is thread-safe on most platforms. + */ +inline auto gmtime(std::time_t time) -> std::tm { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + inline dispatcher(std::time_t t) : time_(t) {} + + inline auto run() -> bool { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } + + inline auto handle(detail::null<>) -> bool { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + inline auto fallback(int res) -> bool { return res == 0; } + +#if !FMT_MSC_VERSION + inline auto fallback(detail::null<>) -> bool { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + auto gt = dispatcher(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +template +inline auto gmtime(sys_time time_point) -> std::tm { + return gmtime(detail::to_time_t(time_point)); +} + +namespace detail { + +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + std::memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + std::memcpy(buf, &digits, len); + } +} + +template +FMT_CONSTEXPR inline auto get_units() -> const char* { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) + return detail::use_utf8 ? "µs" : "us"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "min"; + if (std::is_same>::value) return "h"; + if (std::is_same>::value) return "d"; + return nullptr; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Glibc extensions for formatting numeric values. +enum class pad_type { + // Pad a numeric result string with zeros (the default). + zero, + // Do not pad a numeric result string. + none, + // Pad a numeric result string with spaces. + space, +}; + +template +auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { + if (pad == pad_type::none) return out; + return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); +} + +template +auto write_padding(OutputIt out, pad_type pad) -> OutputIt { + if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; + return out; +} + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + if (begin == end || *begin == '}') return begin; + if (*begin != '%') FMT_THROW(format_error("invalid format")); + auto ptr = begin; + while (ptr != end) { + pad_type pad = pad_type::zero; + 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 '_': + pad = pad_type::space; + ++ptr; + break; + case '-': + pad = pad_type::none; + ++ptr; + break; + } + 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; + } + // Year: + case 'Y': handler.on_year(numeric_system::standard, pad); break; + case 'y': handler.on_short_year(numeric_system::standard); break; + case 'C': handler.on_century(numeric_system::standard); break; + case 'G': handler.on_iso_week_based_year(); break; + case 'g': handler.on_iso_week_based_short_year(); 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': + case 'h': handler.on_abbr_month(); break; + case 'B': handler.on_full_month(); break; + case 'm': handler.on_dec_month(numeric_system::standard, pad); break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard, pad); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard, pad); + break; + case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; + case 'j': handler.on_day_of_year(pad); break; + case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; + case 'e': + handler.on_day_of_month(numeric_system::standard, pad_type::space); + break; + // Hour, minute, second: + case 'H': handler.on_24_hour(numeric_system::standard, pad); break; + case 'I': handler.on_12_hour(numeric_system::standard, pad); break; + case 'M': handler.on_minute(numeric_system::standard, pad); break; + case 'S': handler.on_second(numeric_system::standard, pad); 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(numeric_system::standard); 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 'Y': handler.on_year(numeric_system::alternative, pad); break; + case 'y': handler.on_offset_year(); break; + case 'C': handler.on_century(numeric_system::alternative); break; + 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; + case 'z': handler.on_utc_offset(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 'y': handler.on_short_year(numeric_system::alternative); break; + case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative, pad); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative, pad); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative, pad); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative, pad); + break; + case 'e': + handler.on_day_of_month(numeric_system::alternative, pad_type::space); + break; + 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, pad); break; + case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; + case 'M': handler.on_minute(numeric_system::alternative, pad); break; + case 'S': handler.on_second(numeric_system::alternative, pad); break; + case 'z': handler.on_utc_offset(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; +} + +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +class tm_format_checker : public null_chrono_spec_handler { + private: + bool has_timezone_ = false; + + public: + constexpr explicit tm_format_checker(bool has_timezone) + : has_timezone_(has_timezone) {} + + FMT_NORETURN inline void unsupported() { + FMT_THROW(format_error("no format")); + } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } + FMT_CONSTEXPR void on_tz_name() { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } +}; + +inline auto tm_wday_full_name(int wday) -> const char* { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline auto tm_wday_short_name(int wday) -> const char* { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline auto tm_mon_full_name(int mon) -> const char* { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline auto tm_mon_short_name(int mon) -> const char* { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template +struct has_tm_gmtoff : std::false_type {}; +template +struct has_tm_gmtoff> : std::true_type {}; + +template struct has_tm_zone : std::false_type {}; +template +struct has_tm_zone> : std::true_type {}; + +template ::value)> +bool set_tm_zone(T& time, char* tz) { + time.tm_zone = tz; + return true; +} +template ::value)> +bool set_tm_zone(T&, char*) { + return false; +} + +inline char* utc() { + static char tz[] = "UTC"; + return tz; +} + +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline auto to_nonnegative_int(T value, Int upper) -> Int { + if (!std::is_unsigned::value && + (value < 0 || to_unsigned(value) > to_unsigned(upper))) { + FMT_THROW(format_error("chrono value is out of range")); + } + return static_cast(value); +} +template ::value)> +inline auto to_nonnegative_int(T value, Int upper) -> Int { + auto int_value = static_cast(value); + if (int_value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return int_value; +} + +constexpr auto pow10(std::uint32_t n) -> long long { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +// Counts the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +template () / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template +struct count_fractional_digits { + static constexpr int value = (Num % Den == 0) ? N : 6; +}; + +// Format subseconds which are given as an integer type with an appropriate +// number of digits. +template +void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { + constexpr auto num_fractional_digits = + count_fractional_digits::value; + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, pow10(num_fractional_digits)>>; + + const auto fractional = d - detail::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : detail::duration_cast(fractional).count(); + auto n = static_cast>(subseconds); + const int num_digits = count_digits(n); + + int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); + if (precision < 0) { + FMT_ASSERT(!std::is_floating_point::value, ""); + if (std::ratio_less::value) { + *out++ = '.'; + out = detail::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits); + } + } else if (precision > 0) { + *out++ = '.'; + leading_zeroes = min_of(leading_zeroes, precision); + int remaining = precision - leading_zeroes; + out = detail::fill_n(out, leading_zeroes, '0'); + if (remaining < num_digits) { + int num_truncated_digits = num_digits - remaining; + n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); + if (n != 0) out = format_decimal(out, n, remaining); + return; + } + if (n != 0) { + out = format_decimal(out, n, num_digits); + remaining -= num_digits; + } + out = detail::fill_n(out, remaining, '0'); + } +} + +// Format subseconds which are given as a floating point type with an +// appropriate number of digits. We cannot pass the Duration here, as we +// explicitly need to pass the Rep value in the duration_formatter. +template +void write_floating_seconds(memory_buffer& buf, Duration duration, + int num_fractional_digits = -1) { + using rep = typename Duration::rep; + FMT_ASSERT(std::is_floating_point::value, ""); + + auto val = duration.count(); + + if (num_fractional_digits < 0) { + // For `std::round` with fallback to `round`: + // On some toolchains `std::round` is not available (e.g. GCC 6). + using namespace std; + num_fractional_digits = + count_fractional_digits::value; + if (num_fractional_digits < 6 && static_cast(round(val)) != val) + num_fractional_digits = 6; + } + + fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); +} + +template +class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + bool is_classic_; + OutputIt out_; + const Duration* subsecs_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + auto h = tm_hour(); + auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast(l); + } + + // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. + auto iso_year_weeks(long long curr_year) const noexcept -> int { + auto prev_year = curr_year - 1; + auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + void write2(int value, pad_type pad) { + unsigned int v = to_unsigned(value) % 100; + if (v >= 10) { + const char* d = digits2(v); + *out_++ = *d++; + *out_++ = *d; + } else { + out_ = detail::write_padding(out_, pad); + *out_++ = static_cast('0' + v); + } + } + + void write_year_extended(long long year, pad_type pad) { + // At least 4 characters. + int width = 4; + bool negative = year < 0; + if (negative) { + year = 0 - year; + --width; + } + uint32_or_64_or_128_t n = to_unsigned(year); + const int num_digits = count_digits(n); + if (negative && pad == pad_type::zero) *out_++ = '-'; + if (width > num_digits) + out_ = detail::write_padding(out_, pad, width - num_digits); + if (negative && pad != pad_type::zero) *out_++ = '-'; + out_ = format_decimal(out_, n, num_digits); + } + void write_year(long long year, pad_type pad) { + write_year_extended(year, pad); + } + + void write_utc_offset(long long offset, numeric_system ns) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + if (ns != numeric_system::standard) *out_++ = ':'; + write2(static_cast(offset % 60)); + } + + template ::value)> + void format_utc_offset(const T& tm, numeric_system ns) { + write_utc_offset(tm.tm_gmtoff, ns); + } + template ::value)> + void format_utc_offset(const T&, numeric_system ns) { + write_utc_offset(0, ns); + } + + template ::value)> + void format_tz_name(const T& tm) { + out_ = write_tm_str(out_, tm.tm_zone, loc_); + } + template ::value)> + void format_tz_name(const T&) { + out_ = std::copy_n(utc(), 3, out_); + } + + void format_localized(char format, char modifier = 0) { + out_ = write(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, + const Duration* subsecs = nullptr) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + subsecs_(subsecs), + tm_(tm) {} + + auto out() const -> OutputIt { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month(numeric_system::standard, pad_type::space); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard, pad_type::space); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + write2digits(buf, static_cast(year / 100)); + } else { + offset = 4; + write_year_extended(year, pad_type::zero); + year = 0; + } + write_digit2_separated(buf + 2, static_cast(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } + void on_tz_name() { format_tz_name(tm_); } + + void on_year(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year(), pad); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1, pad); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, + pad); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week, + pad); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year(), pad); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { + write_year(tm_iso_week_year(), pad_type::zero); + } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year(pad_type pad) { + auto yday = tm_yday() + 1; + auto digit1 = yday / 100; + if (digit1 != 0) + write1(digit1); + else + out_ = detail::write_padding(out_, pad); + write2(yday % 100, pad); + } + + void on_day_of_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mday(), pad); + format_localized('d', 'O'); + } + + void on_24_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour(), pad); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12(), pad); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_min(), pad); + format_localized('M', 'O'); + } + + void on_second(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) { + write2(tm_sec(), pad); + if (subsecs_) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, *subsecs_); + if (buf.size() > 1) { + // Remove the leading "0", write something like ".123". + out_ = copy(buf.begin() + 1, buf.end(), out_); + } + } else { + write_fractional_seconds(out_, *subsecs_); + } + } + } else { + // Currently no formatting of subseconds when a locale is set. + format_localized('S', 'O'); + } + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + on_24_hour_time(); + *out_++ = ':'; + on_second(numeric_system::standard, pad_type::zero); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + +struct chrono_format_checker : null_chrono_spec_handler { + bool has_precision_integral = false; + + FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() const { + if (has_precision_integral) + FMT_THROW(format_error("precision not allowed for this argument type")); + } + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template ::value&& has_isfinite::value)> +inline auto isfinite(T) -> bool { + return true; +} + +template ::value)> +inline auto mod(T x, int y) -> T { + return x % static_cast(y); +} +template ::value)> +inline auto mod(T x, int y) -> T { + 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; +}; + +template ::value)> +inline auto get_milliseconds(std::chrono::duration d) + -> std::chrono::duration { + // This may overflow and/or the result may not fit in the target type. +#if FMT_SAFE_DURATION_CAST + using common_seconds_type = + typename std::common_type::type; + auto d_as_common = detail::duration_cast(d); + auto d_as_whole_seconds = + detail::duration_cast(d_as_common); + // This conversion should be nonproblematic. + auto diff = d_as_common - d_as_whole_seconds; + auto ms = detail::duration_cast>(diff); + return ms; +#else + auto s = detail::duration_cast(d); + return detail::duration_cast(d - s); +#endif +} + +template ::value)> +auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { + return write(out, val); +} + +template ::value)> +auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { + auto specs = format_specs(); + specs.precision = precision; + specs.set_type(precision >= 0 ? presentation_type::fixed + : presentation_type::general); + return write(out, val, specs); +} + +template +auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { + return copy(unit.begin(), unit.end(), out); +} + +template +auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +auto format_duration_unit(OutputIt out) -> OutputIt { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + *out++ = '['; + out = write(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; +} + +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get()); + } + inline ~get_locale() { + if (has_locale_) locale_.~locale(); + } + inline operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + +template +struct duration_formatter { + using iterator = basic_appender; + iterator out; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + int precision; + locale_ref locale; + bool localized = false; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using tm_writer_type = tm_writer; + + duration_formatter(iterator o, std::chrono::duration d, + locale_ref loc) + : out(o), val(static_cast(d.count())), locale(loc), 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. + // might need checked conversion (rep!=Rep) + s = detail::duration_cast(std::chrono::duration(val)); + } + + // returns true if nan or inf, writes to out. + auto handle_nan_inf() -> bool { + if (isfinite(val)) return false; + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) + std::copy_n("inf", 3, out); + else + std::copy_n("-inf", 4, out); + return true; + } + + auto days() const -> Rep { return static_cast(s.count() / 86400); } + auto hour() const -> Rep { + return static_cast(mod((s.count() / 3600), 24)); + } + + auto hour12() const -> Rep { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + auto minute() const -> Rep { + return static_cast(mod((s.count() / 60), 60)); + } + auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } + + auto time() const -> std::tm { + 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) return; + *out++ = '-'; + negative = false; + } + + void write(Rep value, int width, pad_type pad = pad_type::zero) { + 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 = detail::count_digits(n); + if (width > num_digits) { + out = detail::write_padding(out, pad, width - num_digits); + } + out = format_decimal(out, n, num_digits); + } + + void write_nan() { std::copy_n("nan", 3, out); } + + template + void format_tm(const tm& time, Callback cb, Args... args) { + if (isnan(val)) return write_nan(); + get_locale loc(localized, locale); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); + } + + void on_text(const Char* begin, const Char* end) { + 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(numeric_system) {} + void on_tz_name() {} + void on_year(numeric_system, pad_type) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system, pad_type) {} + void on_dec0_week_of_year(numeric_system, pad_type) {} + void on_dec1_week_of_year(numeric_system, pad_type) {} + void on_iso_week_of_year(numeric_system, pad_type) {} + void on_day_of_month(numeric_system, pad_type) {} + + void on_day_of_year(pad_type) { + if (handle_nan_inf()) return; + write(days(), 0); + } + + void on_24_hour(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns, pad); + } + + void on_12_hour(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns, pad); + } + + void on_minute(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2, pad); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns, pad); + } + + void on_second(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, std::chrono::duration(val), + precision); + if (negative) *out++ = '-'; + if (buf.size() < 2 || buf[1] == '.') + out = detail::write_padding(out, pad); + out = copy(buf.begin(), buf.end(), out); + } else { + write(second(), 2, pad); + write_fractional_seconds( + out, std::chrono::duration(val), precision); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns, pad); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_12_hour_time); + } + + 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; + on_second(numeric_system::standard, pad_type::zero); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { out = format_duration_unit(out); } +}; + +} // namespace detail + +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; +using year_month_day = std::chrono::year_month_day; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value_; + + public: + weekday() = default; + constexpr explicit weekday(unsigned wd) noexcept + : value_(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned { return value_; } +}; + +class day { + private: + unsigned char value_; + + public: + day() = default; + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class month { + private: + unsigned char value_; + + public: + month() = default; + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class year { + private: + int value_; + + public: + year() = default; + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } +}; + +class year_month_day { + private: + fmt::year year_; + fmt::month month_; + fmt::day day_; + + public: + year_month_day() = default; + constexpr year_month_day(const year& y, const month& m, const day& d) noexcept + : year_(y), month_(m), day_(d) {} + constexpr auto year() const noexcept -> fmt::year { return year_; } + constexpr auto month() const noexcept -> fmt::month { return month_; } + constexpr auto day() const noexcept -> fmt::day { return day_; } +}; +#endif // __cpp_lib_chrono >= 201907 + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); + } + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(static_cast(d)); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); + } + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(static_cast(m)) - 1; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_month(); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(y) - 1900; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_year(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year_month_day val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(val.year()) - 1900; + time.tm_mon = static_cast(static_cast(val.month())) - 1; + time.tm_mday = static_cast(static_cast(val.day())); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(true, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_iso_date(); + return w.out(); + } +}; + +template +struct formatter, Char> { + private: + format_specs specs_; + detail::arg_ref width_ref_; + detail::arg_ref precision_ref_; + basic_string_view fmt_; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + + it = detail::parse_align(it, end, specs_); + if (it == end) return it; + + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } + + auto checker = detail::chrono_format_checker(); + if (*it == '.') { + checker.has_precision_integral = !std::is_floating_point::value; + it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); + } + if (it != end && *it == 'L') { + specs_.set_localized(); + ++it; + } + end = detail::parse_chrono_format(it, end, checker); + fmt_ = {it, detail::to_unsigned(end - it)}; + return end; + } + + template + auto format(std::chrono::duration d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs = specs_; + auto precision = specs.precision; + specs.precision = -1; + auto begin = fmt_.begin(), end = fmt_.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + auto buf = basic_memory_buffer(); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), precision, + precision_ref_, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision); + detail::format_duration_unit(out); + } else { + auto f = + detail::duration_formatter(out, d, ctx.locale()); + f.precision = precision; + f.localized = specs_.localized(); + detail::parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } +}; + +template struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + basic_string_view fmt_ = + detail::string_literal(); + + protected: + auto localized() const -> bool { return specs_.localized(); } + FMT_CONSTEXPR void set_localized() { specs_.set_localized(); } + + FMT_CONSTEXPR auto do_parse(parse_context& ctx, bool has_timezone) + -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + + it = detail::parse_align(it, end, specs_); + if (it == end) return it; + + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } + + if (*it == 'L') { + specs_.set_localized(); + ++it; + } + + end = detail::parse_chrono_format(it, end, + detail::tm_format_checker(has_timezone)); + // Replace the default format string only if the new spec is not empty. + if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; + return end; + } + + template + auto do_format(const std::tm& tm, FormatContext& ctx, + const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs = specs_; + auto buf = basic_memory_buffer(); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + + auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = detail::tm_writer, Char, Duration>( + loc, out, tm, subsecs); + detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, detail::has_tm_gmtoff::value); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + return do_format(tm, ctx, nullptr); + } +}; + +// DEPRECATED! Reversed order of template parameters. +template +struct formatter, Char> : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, true); + } + + template + auto format(sys_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + std::tm tm = gmtime(val); + using period = typename Duration::period; + if (detail::const_check( + period::num == 1 && period::den == 1 && + !std::is_floating_point::value)) { + detail::set_tm_zone(tm, detail::utc()); + return formatter::format(tm, ctx); + } + Duration epoch = val.time_since_epoch(); + Duration subsecs = detail::duration_cast( + epoch - detail::duration_cast(epoch)); + if (subsecs.count() < 0) { + auto second = detail::duration_cast(std::chrono::seconds(1)); + if (tm.tm_sec != 0) { + --tm.tm_sec; + } else { + tm = gmtime(val - second); + detail::set_tm_zone(tm, detail::utc()); + } + subsecs += second; + } + return formatter::do_format(tm, ctx, &subsecs); + } +}; + +template +struct formatter, Char> + : formatter, Char> { + template + auto format(utc_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format( + detail::utc_clock::to_sys(val), ctx); + } +}; + +template +struct formatter, Char> + : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, false); + } + + template + auto format(local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time_since_epoch = val.time_since_epoch(); + auto seconds_since_epoch = + detail::duration_cast(time_since_epoch); + // Use gmtime to prevent time zone conversion since local_time has an + // unspecified time zone. + std::tm t = gmtime(seconds_since_epoch.count()); + using period = typename Duration::period; + if (period::num == 1 && period::den == 1 && + !std::is_floating_point::value) { + return formatter::format(t, ctx); + } + auto subsecs = + detail::duration_cast(time_since_epoch - seconds_since_epoch); + return formatter::do_format(t, ctx, &subsecs); + } +}; + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/deps/fmt/include/fmt/color.h b/deps/fmt/include/fmt/color.h new file mode 100644 index 00000000000..638f15b43f3 --- /dev/null +++ b/deps/fmt/include/fmt/color.h @@ -0,0 +1,637 @@ +// 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 +FMT_BEGIN_EXPORT + +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, + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, +}; + +// 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 { + constexpr rgb() : r(0), g(0), b(0) {} + constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + constexpr rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + 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 detail { + +// A bit-packed variant of an RGB color, a terminal color, or unset color. +// see text_style for the bit-packing scheme. +struct color_type { + constexpr color_type() noexcept = default; + constexpr color_type(color rgb_color) noexcept + : value_(static_cast(rgb_color) | (1 << 24)) {} + constexpr color_type(rgb rgb_color) noexcept + : color_type(static_cast( + (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} + constexpr color_type(terminal_color term_color) noexcept + : value_(static_cast(term_color) | (3 << 24)) {} + + constexpr auto is_terminal_color() const noexcept -> bool { + return (value_ & (1 << 25)) != 0; + } + + constexpr auto value() const noexcept -> uint32_t { + return value_ & 0xFFFFFF; + } + + constexpr color_type(uint32_t value) noexcept : value_(value) {} + + uint32_t value_ = 0; +}; +} // namespace detail + +/// A text style consisting of foreground and background colors and emphasis. +class text_style { + // The information is packed as follows: + // ┌──┐ + // │ 0│─┐ + // │..│ ├── foreground color value + // │23│─┘ + // ├──┤ + // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's + // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) + // ├──┤ + // │26│──── overflow bit, always zero (see below) + // ├──┤ + // │27│─┐ + // │..│ │ + // │50│ │ + // ├──┤ │ + // │51│ ├── background color (same format as the foreground color) + // │52│ │ + // ├──┤ │ + // │53│─┘ + // ├──┤ + // │54│─┐ + // │..│ ├── emphases + // │61│─┘ + // ├──┤ + // │62│─┬── unused + // │63│─┘ + // └──┘ + // The overflow bits are there to make operator|= efficient. + // When ORing, we must throw if, for either the foreground or background, + // one style specifies a terminal color and the other specifies any color + // (terminal or RGB); in other words, if one discriminator is 11 and the + // other is 11 or 01. + // + // We do that check by adding the styles. Consider what adding does to each + // possible pair of discriminators: + // 00 + 00 = 000 + // 01 + 00 = 001 + // 11 + 00 = 011 + // 01 + 01 = 010 + // 11 + 01 = 100 (!!) + // 11 + 11 = 110 (!!) + // In the last two cases, the ones we want to catch, the third bit——the + // overflow bit——is set. Bingo. + // + // We must take into account the possible carry bit from the bits + // before the discriminator. The only potentially problematic case is + // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry + // bit is impossible in that case, because 00 (unset color) means the + // 24 bits that precede the discriminator are all zero. + // + // This test can be applied to both colors simultaneously. + + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept + : style_(static_cast(em) << 54) {} + + FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { + if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) + report_error("can't OR a terminal color"); + style_ |= rhs.style_; + return *this; + } + + friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) + -> text_style { + return lhs |= rhs; + } + + FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { + return style_ == rhs.style_; + } + + FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { + return !(*this == rhs); + } + + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { + return (style_ & (1 << 24)) != 0; + } + FMT_CONSTEXPR auto has_background() const noexcept -> bool { + return (style_ & (1ULL << 51)) != 0; + } + FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { + return (style_ >> 54) != 0; + } + FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return style_ & 0x3FFFFFF; + } + FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { + FMT_ASSERT(has_background(), "no background specified for this style"); + return (style_ >> 27) & 0x3FFFFFF; + } + FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return static_cast(style_ >> 54); + } + + private: + FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} + + friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept + -> text_style; + + friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept + -> text_style; + + uint64_t style_ = 0; +}; + +/// Creates a text style from the foreground (text) color. +FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept + -> text_style { + return foreground.value_; +} + +/// Creates a text style from the background color. +FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept + -> text_style { + return static_cast(background.value_) << 27; +} + +FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept + -> text_style { + return text_style(lhs) | rhs; +} + +namespace detail { + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(color_type text_color, + const char* esc) noexcept { + // If we have a terminal color, we need to output another escape code + // sequence. + if (text_color.is_terminal_color()) { + bool is_background = esc == string_view("\x1b[48;2;"); + uint32_t value = text_color.value(); + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + 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()); + 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) noexcept { + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; + + size_t index = 0; + for (size_t i = 0; i < num_emphases; ++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 noexcept { return buffer; } + + FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } + FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { + return buffer + basic_string_view(buffer).size(); + } + + private: + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) 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); + } + static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept + -> bool { + return static_cast(em) & static_cast(mask); + } +}; + +template +FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept + -> ansi_color_escape { + return ansi_color_escape(foreground, "\x1b[38;2;"); +} + +template +FMT_CONSTEXPR auto make_background_color(color_type background) noexcept + -> ansi_color_escape { + return ansi_color_escape(background, "\x1b[48;2;"); +} + +template +FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept + -> ansi_color_escape { + return ansi_color_escape(em); +} + +template inline void reset_color(buffer& buffer) { + auto reset_color = string_view("\x1b[0m"); + buffer.append(reset_color.begin(), reset_color.end()); +} + +template struct styled_arg : view { + const T& value; + text_style style; + styled_arg(const T& v, text_style s) : value(v), style(s) {} +}; + +template +void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, + basic_format_args> args) { + if (ts.has_emphasis()) { + auto emphasis = make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + auto foreground = make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + auto background = make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + vformat_to(buf, fmt, args); + if (ts != text_style()) reset_color(buf); +} +} // namespace detail + +inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); +} + +/** + * 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 +void print(FILE* f, text_style ts, format_string fmt, T&&... args) { + vprint(f, ts, fmt.str, vargs{{args...}}); +} + +/** + * 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 +void print(text_style ts, format_string fmt, T&&... args) { + return print(stdout, ts, fmt, std::forward(args)...); +} + +inline auto vformat(text_style ts, string_view fmt, format_args args) + -> std::string { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + return fmt::to_string(buf); +} + +/** + * 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); + * ``` + */ +template +inline auto format(text_style ts, format_string fmt, T&&... args) + -> std::string { + return fmt::vformat(ts, fmt.str, vargs{{args...}}); +} + +/// Formats a string with the given text_style and writes the output to `out`. +template ::value)> +auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) + -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, fmt, args); + return detail::get_iterator(buf, out); +} + +/** + * Formats arguments with the given text style, writes the result to the output + * iterator `out` and returns the iterator past the end of the output range. + * + * **Example**: + * + * std::vector out; + * fmt::format_to(std::back_inserter(out), + * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + */ +template ::value)> +inline auto format_to(OutputIt out, text_style ts, format_string fmt, + T&&... args) -> OutputIt { + return vformat_to(out, ts, fmt.str, vargs{{args...}}); +} + +template +struct formatter, Char> : formatter { + template + auto format(const detail::styled_arg& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto& ts = arg.style; + auto out = ctx.out(); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + out = detail::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + out = detail::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + out = detail::copy(background.begin(), background.end(), out); + } + out = formatter::format(arg.value, ctx); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = detail::copy(reset_color.begin(), reset_color.end(), out); + } + return out; + } +}; + +/** + * Returns an argument that will be formatted using ANSI escape sequences, + * to be used in a formatting function. + * + * **Example**: + * + * fmt::print("Elapsed time: {0:.2f} seconds", + * fmt::styled(1.23, fmt::fg(fmt::color::green) | + * fmt::bg(fmt::color::blue))); + */ +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/deps/fmt/include/fmt/compile.h b/deps/fmt/include/fmt/compile.h new file mode 100644 index 00000000000..08d9427ff24 --- /dev/null +++ b/deps/fmt/include/fmt/compile.h @@ -0,0 +1,539 @@ +// 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_ + +#ifndef FMT_MODULE +# include // std::back_inserter +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// A compile-time string which is compiled into fast formatting code. +FMT_EXPORT class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +namespace detail { + +/** + * Converts a string literal `s` into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires C++17 + * `constexpr if` compiler support. + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * std::string s = fmt::format(FMT_COMPILE("{}"), 42); + */ +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) +#else +# define FMT_COMPILE(s) FMT_STRING(s) +#endif + +template +auto first(const T& value, const Tail&...) -> const T& { + return value; +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return detail::get(rest...); +} + +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_static_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return -1; +} +# endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +# endif + (void)name; + return -1; +} + +template +constexpr int get_arg_index_by_name(basic_string_view name, + type_list) { + return get_arg_index_by_name(name); +} + +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 is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + *out++ = value; + return out; + } +}; + +// This ensures that the argument type is convertible to `const T&`. +template +constexpr const T& get_arg_checked(const Args&... args) { + const auto& arg = detail::get(args...); + if constexpr (detail::is_named_arg>()) { + return arg.value; + } else { + return arg; + } +} + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + const T& arg = get_arg_checked(args...); + if constexpr (std::is_convertible>::value) { + auto s = basic_string_view(arg); + return copy(s.begin(), s.end(), out); + } else { + return write(out, arg); + } + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument with name. +template struct runtime_named_field { + using char_type = Char; + basic_string_view name; + + template + constexpr static bool try_format_argument( + OutputIt& out, + // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 + [[maybe_unused]] basic_string_view arg_name, const T& arg) { + if constexpr (is_named_arg::type>::value) { + if (arg_name == arg.name) { + out = write(out, arg.value); + return true; + } + } + return false; + } + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + FMT_THROW(format_error("argument with specified name is not found")); + } + return out; + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + formatter fmt; + + template + constexpr FMT_INLINE OutputIt format(OutputIt out, + const Args&... args) const { + const auto& vargs = + fmt::make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(get_arg_checked(args...), ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +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 fmt); + +template +constexpr auto parse_tail(T head, S fmt) { + if constexpr (POS != basic_string_view(fmt).size()) { + constexpr auto tail = compile_format_string(fmt); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +enum { manual_indexing_id = -1 }; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int next_arg_id) { + str.remove_prefix(pos); + auto ctx = + compile_parse_context(str, max_value(), nullptr, next_arg_id); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + fmt::detail::to_unsigned(end - str.data()), + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + arg_id_kind kind; + arg_ref arg_id; + + constexpr int on_auto() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int on_index(int id) { + kind = arg_id_kind::index; + arg_id = arg_ref(id); + return 0; + } + constexpr int on_name(basic_string_view id) { + kind = arg_id_kind::name; + arg_id = arg_ref(id); + return 0; + } +}; + +template struct parse_arg_id_result { + arg_id_kind kind; + arg_ref arg_id; + const Char* arg_id_end; +}; + +template +constexpr auto parse_arg_id(const Char* begin, const Char* end) { + auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; + auto arg_id_end = parse_arg_id(begin, end, handler); + return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; +} + +template struct field_type { + using type = remove_cvref_t; +}; + +template +struct field_type::value>> { + using type = remove_cvref_t; +}; + +template +constexpr auto parse_replacement_field_then_tail(S fmt) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(fmt); + constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); + if constexpr (c == '}') { + return parse_tail( + field::type, ARG_INDEX>(), fmt); + } else if constexpr (c != ':') { + FMT_THROW(format_error("expected ':'")); + } else { + constexpr auto result = parse_specs::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + fmt); + } + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S fmt) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(fmt); + if constexpr (str[POS] == '{') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '{' in format string")); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), fmt); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail, Args, + POS + 1, ID, next_id>(fmt); + } else { + constexpr auto arg_id_result = + parse_arg_id(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + constexpr char_type c = + arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); + static_assert(c == '}' || c == ':', "missing '}' in format string"); + if constexpr (arg_id_result.kind == arg_id_kind::index) { + static_assert( + ID == manual_indexing_id || ID == 0, + "cannot switch from automatic to manual argument indexing"); + constexpr auto arg_index = arg_id_result.arg_id.index; + return parse_replacement_field_then_tail, + Args, arg_id_end_pos, + arg_index, manual_indexing_id>( + fmt); + } else if constexpr (arg_id_result.kind == arg_id_kind::name) { + constexpr auto arg_index = + get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); + if constexpr (arg_index >= 0) { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail< + decltype(get_type::value), Args, arg_id_end_pos, + arg_index, next_id>(fmt); + } else if constexpr (c == '}') { + return parse_tail( + runtime_named_field{arg_id_result.arg_id.name}, fmt); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } + } + } else if constexpr (str[POS] == '}') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '}' in format string")); + return parse_tail(make_text(str, POS, 1), fmt); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), fmt); + } else { + return parse_tail(code_unit{str[POS]}, fmt); + } + } +} + +template ::value)> +constexpr auto compile(S fmt) { + constexpr auto str = basic_string_view(fmt); + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>(fmt); + return result; + } +} +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +} // namespace detail + +FMT_BEGIN_EXPORT + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + auto s = std::basic_string(); + cf.format(std::back_inserter(s), args...); + return s; +} + +template ::value)> +constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { + if constexpr (std::is_same::value) { + constexpr auto str = basic_string_view(S()); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } + } + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format( + static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format(compiled, std::forward(args)...); + } +} + +template ::value)> +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format_to( + out, static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format_to(out, compiled, std::forward(args)...); + } +} +#endif + +template ::value)> +auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); + return {buf.out(), buf.count()}; +} + +template ::value)> +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) + -> size_t { + auto buf = detail::counting_buffer<>(); + fmt::format_to(appender(buf), fmt, args...); + return buf.count(); +} + +template ::value)> +void print(std::FILE* f, const S& fmt, const Args&... args) { + auto buf = memory_buffer(); + fmt::format_to(appender(buf), fmt, args...); + detail::print(f, {buf.data(), buf.size()}); +} + +template ::value)> +void print(const S& fmt, const Args&... args) { + print(stdout, fmt, args...); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals +#endif + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/deps/fmt/include/fmt/core.h b/deps/fmt/include/fmt/core.h new file mode 100644 index 00000000000..8ca735f0c00 --- /dev/null +++ b/deps/fmt/include/fmt/core.h @@ -0,0 +1,5 @@ +// This file is only provided for compatibility and may be removed in future +// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h +// otherwise. + +#include "format.h" diff --git a/deps/fmt/include/fmt/fmt.license.rst b/deps/fmt/include/fmt/fmt.license.rst new file mode 100644 index 00000000000..1cd1ef92696 --- /dev/null +++ b/deps/fmt/include/fmt/fmt.license.rst @@ -0,0 +1,27 @@ +Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors + +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/deps/fmt/include/fmt/format-inl.h b/deps/fmt/include/fmt/format-inl.h new file mode 100644 index 00000000000..a1e01661178 --- /dev/null +++ b/deps/fmt/include/fmt/format-inl.h @@ -0,0 +1,1948 @@ +// 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_ + +#ifndef FMT_MODULE +# include +# include // errno +# include +# include +# include +#endif + +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) +# include // _isatty +#endif + +#include "format.h" + +#if FMT_USE_LOCALE +# include +#endif + +#ifndef FMT_FUNC +# define FMT_FUNC +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails. + fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + abort(); +} + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) noexcept { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_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. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = appender(out); + if (message.size() <= inline_buffer_size - error_code_size) + fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); + fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + FMT_ASSERT(out.size() <= inline_buffer_size, ""); +} + +FMT_FUNC void do_report_error(format_func func, int error_code, + const char* message) noexcept { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_all because the latter may throw. + if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { + size_t written = std::fwrite(ptr, 1, count, stream); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +#if FMT_USE_LOCALE +using std::locale; +using std::numpunct; +using std::use_facet; + +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} +#else +struct locale {}; +template struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template Facet use_facet(locale) { return {}; } +#endif // FMT_USE_LOCALE + +template auto locale_ref::get() const -> Locale { + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); +} + +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto&& facet = use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; +} +template +FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { + return use_facet>(loc.get()).decimal_point(); +} + +#if FMT_USE_LOCALE +FMT_FUNC auto write_loc(appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool { + auto locale = loc.get(); + // We cannot use the num_put facet because it may produce output in + // a wrong encoding. + using facet = format_facet; + if (std::has_facet(locale)) + return use_facet(locale).put(out, value, specs); + return facet(locale).put(out, value, specs); +} +#endif +} // namespace detail + +FMT_FUNC void report_error(const char* message) { +#if FMT_USE_EXCEPTIONS + // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings + // from MSVC. + FMT_THROW(format_error(message)); +#else + fputs(message, stderr); + abort(); +#endif +} + +template typename Locale::id format_facet::id; + +template format_facet::format_facet(Locale& loc) { + auto& np = detail::use_facet>(loc); + grouping_ = np.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); +} + +#if FMT_USE_LOCALE +template <> +FMT_API FMT_FUNC auto format_facet::do_put( + appender out, loc_value val, const format_specs& specs) const -> bool { + return val.visit( + detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); +} +#endif + +FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(fmt, args)); +} + +namespace detail { + +template +inline auto operator==(basic_fp x, basic_fp y) -> bool { + return x.f == y.f && x.e == y.e; +} + +// Compilers should be able to optimize this into the ror instruction. +FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { + r &= 31; + return (n >> r) | (n << (32 - r)); +} +FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { + r &= 63; + return (n >> r) | (n << (64 - r)); +} + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { + return umul128_upper64(static_cast(x) << 32, y); +} + +// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { + uint64_t high = x * y.high(); + uint128_fallback high_low = umul128(x, y.low()); + return {high + high_low.high(), high_low.low()}; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { + return x * y; +} + +// Various fast log computations. +inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { + FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); + return (e * 631305 - 261663) >> 21; +} + +FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { + uint32_t divisor; + int shift_amount; +} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; + +// Replaces n by floor(n / pow(10, N)) returning true if and only if n is +// divisible by pow(10, N). +// Precondition: n <= pow(10, N + 1). +template +auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { + // The numbers below are chosen such that: + // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, + // 2. nm mod 2^k < m if and only if n is divisible by d, + // where m is magic_number, k is shift_amount + // and d is divisor. + // + // Item 1 is a common technique of replacing division by a constant with + // multiplication, see e.g. "Division by Invariant Integers Using + // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set + // to ceil(2^k/d) for large enough k. + // The idea for item 2 originates from Schubfach. + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + n *= magic_number; + const uint32_t comparison_mask = (1u << info.shift_amount) - 1; + bool result = (n & comparison_mask) < magic_number; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + return (n * magic_number) >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { + // 1374389535 = ceil(2^37/100) + return static_cast((static_cast(n) * 1374389535) >> 37); +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { + // 2361183241434822607 = ceil(2^(64+7)/1000) + return umul128_upper64(n, 2361183241434822607ull) >> 7; +} + +// Various subroutines using pow10 cache +template struct cache_accessor; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint64_t; + + static auto get_cached_power(int k) noexcept -> uint64_t { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + static constexpr const uint64_t pow10_significands[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, + 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, + 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, + 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, + 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, + 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, + 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; + return pow10_significands[k - float_info::min_k]; + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { + auto r = umul96_upper64(u, cache); + return {static_cast(r >> 32), + static_cast(r) == 0}; + } + + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { + return static_cast(cache >> (64 - 1 - beta)); + } + + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul96_lower64(two_f, cache); + return {((r >> (64 - beta)) & 1) != 0, + static_cast(r >> (32 - beta)) == 0}; + } + + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return static_cast( + (cache - (cache >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta)); + } + + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return static_cast( + (cache + (cache >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta)); + } + + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return (static_cast( + cache >> (64 - num_significand_bits() - 2 - beta)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint128_fallback; + + static auto get_cached_power(int k) noexcept -> uint128_fallback { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + + static constexpr const uint128_fallback pow10_significands[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0x9f4f2726179a2245, 0x01d762422c946591}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, + {0xacb92ed9397bf996, 0x49c2c37f07965405}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, + {0x83c7088e1aab65db, 0x792667c6da79e0fb}, + {0xa4b8cab1a1563f52, 0x577001b891185939}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0x80b05e5ac60b6178, 0x544f8158315b05b5}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, + {0xfb5878494ace3a5f, 0x04ab48a04065c724}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, + {0xf5746577930d6500, 0xca8f44ec7ee3647a}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, + {0xea1575143cf97226, 0xf52d09d71a3293be}, + {0x924d692ca61be758, 0x593c2626705f9c57}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, + {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, + {0x8b865b215899f46c, 0xbd79e0d20082ee75}, + {0xae67f1e9aec07187, 0xecd8590680a3aa12}, + {0xda01ee641a708de9, 0xe80e6f4820cc9496}, + {0x884134fe908658b2, 0x3109058d147fdcde}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0xcfe87f7cef46ff16, 0xe612641865679a64}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, + {0xa26da3999aef7749, 0xe3be5e330f38f09e}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, + {0xc646d63501a1511d, 0xb281e1fd541501b9}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, + {0x9ae757596946075f, 0x3375788de9b06959}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, + {0xbd176620a501fbff, 0xb650e5a93bc3d899}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, + {0x93ba47c980e98cdf, 0xc66f336c36b10138}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, + {0x9043ea1ac7e41392, 0x87c89837ad68db30}, + {0xb454e4a179dd1877, 0x29babe4598c311fc}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, + {0xdc21a1171d42645d, 0x76707543f4fa1f74}, + {0x899504ae72497eba, 0x6a06494a791c53a9}, + {0xabfa45da0edbde69, 0x0487db9d17636893}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xa7f26836f282b732, 0x8e6cac7768d7141f}, + {0xd1ef0244af2364ff, 0x3207d795430cd927}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, + {0xcd036837130890a1, 0x36dba887c37a8c10}, + {0x802221226be55a64, 0xc2494954da2c978a}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, + {0x9c69a97284b578d7, 0xff2a760414536efc}, + {0xc38413cf25e2d70d, 0xfef5138519684abb}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, + {0xba756174393d88df, 0x94f971119aeef9e5}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, + {0x91abb422ccb812ee, 0xac62e055c10ab33b}, + {0xb616a12b7fe617aa, 0x577b986b314d600a}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, + {0x8e41ade9fbebc27d, 0x14588f13be847308}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, + {0x8aec23d680043bee, 0x25de7bb9480d5855}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0x87aa9aff79042286, 0x90fb44d2f05d0843}, + {0xa99541bf57452b28, 0x353a1607ac744a54}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, + {0x847c9b5d7c2e09b7, 0x69956135febada12}, + {0xa59bc234db398c25, 0x43fab9837e699096}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, + {0x9defbf01b061adab, 0x3a0888136afa64a8}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, + {0xbc4665b596706114, 0x873d5d9f0dde1fef}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, + {0x8fa475791a569d10, 0xf96e017d694487bd}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, + {0xe070f78d3927556a, 0x85bbe253f47b1418}, + {0x8c469ab843b89562, 0x93956d7478ccec8f}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, + {0x88fcf317f22241e2, 0x441fece3bdf81f04}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, + {0x85c7056562757456, 0xf6872d5667844e4a}, + {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, + {0xd106f86e69d785c7, 0xe13336d701beba53}, + {0x82a45b450226b39c, 0xecc0024661173474}, + {0xa34d721642b06084, 0x27f002d7f95d0191}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, + {0xff290242c83396ce, 0x7e67047175a15272}, + {0x9f79a169bd203e41, 0x0f0062c6e984d387}, + {0xc75809c42c684dd1, 0x52c07b78a3e60869}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, + {0xc2abf989935ddbfe, 0x6acff893d00ea436}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, + {0x98165af37b2153de, 0xc3727a337a8b704b}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, + {0xeda2ee1c7064130c, 0x1162def06f79df74}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xb10d8e1456105dad, 0x7425a83e872c5f48}, + {0xdd50f1996b947518, 0xd12f124e28f7771a}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, + {0x8714a775e3e95c78, 0x65acfaec34810a72}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, + {0xd31045a8341ca07c, 0x1ede48111209a051}, + {0x83ea2b892091e44d, 0x934aed0aab460433}, + {0xa4e4b66b68b65d60, 0xf81da84d56178540}, + {0xce1de40642e3f4b9, 0x36251260ab9d668f}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, + {0xa1075a24e4421730, 0xb24cf65b8612f820}, + {0xc94930ae1d529cfc, 0xdee033f26797b628}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, + {0xea53df5fd18d5513, 0x84c86189216dc5ee}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, + {0xdf78e4b2bd342cf6, 0x914da9246b255417}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, + {0xae9672aba3d0c320, 0xa184ac2473b529b2}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, + {0x8865899617fb1871, 0x7e2fa67c7a658893}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, + {0xd51ea6fa85785631, 0x552a74227f3ea566}, + {0x8533285c936b35de, 0xd53a88958f872760}, + {0xa67ff273b8460356, 0x8a892abaf368f138}, + {0xd01fef10a657842c, 0x2d2b7569b0432d86}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, + {0xcb3f2f7642717713, 0x241c70a936219a74}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, + {0x9ec95d1463e8a506, 0xf4363804324a40ab}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, + {0x976e41088617ca01, 0xd5be0503e085d814}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, + {0x906a617d450187e2, 0x27fb2b80668b24c6}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, + {0xe1a63853bbd26451, 0x5e7873f8a0396974}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, + {0xac2820d9623bf429, 0x546345fa9fbdcd45}, + {0xd732290fbacaf133, 0xa97c177947ad4096}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, + {0xa0555e361951c366, 0xd7e105bcc3326220}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, + {0xfa856334878fc150, 0xb14f98f6f0feb952}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, + {0xeeea5d5004981478, 0x1858ccfce06cac75}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xbaa718e68396cffd, 0xd30560258f54e6bb}, + {0xe950df20247c83fd, 0x47c6b82ef32a206a}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, + {0xb6472e511c81471d, 0xe0133fe4adf8e953}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, + {0xb201833b35d63f73, 0x2cd2cc6551e513db}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, + {0x8b112e86420f6191, 0xfb04afaf27faf783}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, + {0xd94ad8b1c7380874, 0x18375281ae7822bd}, + {0x87cec76f1c830548, 0x8f2293910d0b15b6}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, + {0xd433179d9c8cb841, 0x5fa60692a46151ec}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, + {0xa5c7ea73224deff3, 0x12b9b522906c0801}, + {0xcf39e50feae16bef, 0xd768226b34870a01}, + {0x81842f29f2cce375, 0xe6a1158300d46641}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, + {0xc5a05277621be293, 0xc7098b7305241886}, + {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}, + {0x9a65406d44a5c903, 0x737f74f1dc043329}, + {0xc0fe908895cf3b44, 0x505f522e53053ff3}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0}, + {0x96c6e0eab509e64d, 0x5eca783430dc19f6}, + {0xbc789925624c5fe0, 0xb67d16413d132073}, + {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890}, + {0x933e37a534cbaae7, 0x8e91b962f7b6f15a}, + {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1}, + {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d}, + {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2}, + {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e}, + {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, + {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, + {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, + {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0} +#endif + }; + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return pow10_significands[k - float_info::min_k]; +#else + static constexpr const uint64_t powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info::min_k; + int offset = k - kb; + + // Get base cache. + uint128_fallback base_cache = pow10_significands[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = powers_of_5_64[offset]; + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); + return {recovered_cache.high(), recovered_cache.low() + 1}; +#endif + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { + auto r = umul192_upper128(u, cache); + return {r.high(), r.low() == 0}; + } + + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { + return static_cast(cache.high() >> (64 - 1 - beta)); + } + + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); + + auto r = umul192_lower128(two_f, cache); + return {((r.high() >> (64 - beta)) & 1) != 0, + ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; + } + + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return (cache.high() - + (cache.high() >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta); + } + + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return (cache.high() + + (cache.high() >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta); + } + + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { + return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + + 1) / + 2; + } +}; + +FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback { + return cache_accessor::get_cached_power(k); +} + +// Various integer checks +template +auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { + const int case_shorter_interval_left_endpoint_lower_threshold = 2; + const int case_shorter_interval_left_endpoint_upper_threshold = 3; + return exponent >= case_shorter_interval_left_endpoint_lower_threshold && + exponent <= case_shorter_interval_left_endpoint_upper_threshold; +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { + FMT_ASSERT(n != 0, ""); + // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. + constexpr uint32_t mod_inv_5 = 0xcccccccd; + constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 + + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; + } + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + + // This magic number is ceil(2^90 / 10^8). + constexpr uint64_t magic_number = 12379400392853802749ull; + auto nm = umul128(n, magic_number); + + // Is n is divisible by 10^8? + if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + // If yes, work with the quotient... + auto n32 = static_cast(nm.high() >> (90 - 64)); + // ... and use the 32 bit variant of the function + int s = remove_trailing_zeros(n32, 8); + n = n32; + return s; + } + + // If n is not divisible by 10^8, work with n itself. + constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd; + constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5 + + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; + } + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; + } + + return s; +} + +// The main algorithm for shorter interval case +template +FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { + decimal_fp ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor::cache_entry_type; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + + auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( + cache, beta); + auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( + cache, beta); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor::compute_round_up_for_shorter_interval_case(cache, + beta); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info::shorter_interval_tie_lower_threshold && + exponent <= float_info::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template auto to_decimal(T x) noexcept -> decimal_fp { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info::carrier_uint; + using cache_entry_type = typename cache_accessor::cache_entry_type; + auto br = bit_cast(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast(1) << num_significand_bits()) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = + static_cast((br & exponent_mask()) >> num_significand_bits()); + + if (exponent != 0) { // Check if normal. + exponent -= exponent_bias() + num_significand_bits(); + + // Shorter interval case; proceed like Schubfach. + // In fact, when exponent == 1 and significand == 0, the interval is + // regular. However, it can be shown that the end-results are anyway same. + if (significand == 0) return shorter_interval_case(exponent); + + significand |= (static_cast(1) << num_significand_bits()); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = + std::numeric_limits::min_exponent - num_significand_bits() - 1; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai. + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor::compute_delta(cache, beta); + const carrier_uint two_fc = significand << 1; + + // For the case of binary32, the result of integer check is not correct for + // 29711844 * 2^-82 + // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 + // and 29711844 * 2^-81 + // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, + // and they are the unique counterexamples. However, since 29711844 is even, + // this does not cause any problem for the endpoints calculations; it can only + // cause a problem when we need to perform integer check for the center. + // Fortunately, with these inputs, that branch is never executed, so we are + // fine. + const typename cache_accessor::compute_mul_result z_mul = + cache_accessor::compute_mul((two_fc | 1) << beta, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary. + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here. + decimal_fp ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); + uint32_t r = static_cast(z_mul.result - float_info::big_divisor * + ret_value.significand); + + if (r < deltai) { + // Exclude the right endpoint if necessary. + if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { + --ret_value.significand; + r = float_info::big_divisor; + goto small_divisor_case_label; + } + } else if (r > deltai) { + goto small_divisor_case_label; + } else { + // r == deltai; compare fractional parts. + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); + + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) + goto small_divisor_case_label; + } + ret_value.exponent = minus_k + float_info::kappa + 1; + + // We may need to remove trailing zeros. + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor. + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info::kappa; + + uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + + // Is dist divisible by 10^kappa? + const bool divisible_by_small_divisor = + check_divisibility_and_divide_by_pow10::kappa>(dist); + + // Add dist / 10^kappa to the significand. + ret_value.significand += dist; + + if (!divisible_by_small_divisor) return ret_value; + + // Check z^(f) >= epsilon^(f). + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number. + const auto y_mul = cache_accessor::compute_mul_parity(two_fc, cache, beta); + + // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), + // or equivalently, when y is an integer. + if (y_mul.parity != approx_y_parity) + --ret_value.significand; + else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) + --ret_value.significand; + return ret_value; +} +} // namespace dragonbox +} // namespace detail + +template <> struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { + return ctx.begin(); + } + + auto format(const detail::bigint& n, format_context& ctx) const + -> format_context::iterator { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = fmt::format_to(out, FMT_STRING("{:x}"), value); + first = false; + continue; + } + out = fmt::format_to(out, FMT_STRING("{:08x}"), value); + } + if (n.exp_ > 0) + out = fmt::format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return true; + }); + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + const char* message) noexcept { + FMT_TRY { + auto ec = std::error_code(error_code, std::generic_category()); + detail::write(appender(out), std::system_error(ec, message).what()); + return; + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void report_system_error(int error_code, + const char* message) noexcept { + do_report_error(format_system_error, error_code, message); +} + +FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + return to_string(buffer); +} + +namespace detail { + +FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc) { + auto out = appender(buf); + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) + return args.get(0).visit(default_arg_formatter{out}); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} + +template struct span { + T* data; + size_t size; +}; + +template auto flockfile(F* f) -> decltype(_lock_file(f)) { + _lock_file(f); +} +template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { + _unlock_file(f); +} + +#ifndef getc_unlocked +template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { + return _fgetc_nolock(f); +} +#endif + +template +struct has_flockfile : std::false_type {}; + +template +struct has_flockfile()))>> + : std::true_type {}; + +// A FILE wrapper. F is FILE defined as a template parameter to make system API +// detection work. +template class file_base { + public: + F* file_; + + public: + file_base(F* file) : file_(file) {} + operator F*() const { return file_; } + + // Reads a code unit from the stream. + auto get() -> int { + int result = getc_unlocked(file_); + if (result == EOF && ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); + return result; + } + + // Puts the code unit back into the stream buffer. + void unget(char c) { + if (ungetc(c, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); + } + + void flush() { fflush(this->file_); } +}; + +// A FILE wrapper for glibc. +template class glibc_file : public file_base { + private: + enum { + line_buffered = 0x200, // _IO_LINE_BUF + unbuffered = 2 // _IO_UNBUFFERED + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_IO_write_ptr; + } + + // Returns the file's read buffer. + auto get_read_buffer() const -> span { + auto ptr = this->file_->_IO_read_ptr; + return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; + } + + // Returns the file's write buffer. + auto get_write_buffer() const -> span { + auto ptr = this->file_->_IO_write_ptr; + return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; + } + + void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + char* end = this->file_->_IO_write_end; + return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); + } + + void flush() { fflush_unlocked(this->file_); } +}; + +// A FILE wrapper for Apple's libc. +template class apple_file : public file_base { + private: + enum { + line_buffered = 1, // __SNBF + unbuffered = 2 // __SLBF + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_p) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_p; + ++this->file_->_w; + } + + auto get_read_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_r)}; + } + + auto get_write_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_bf._base + this->file_->_bf._size - + this->file_->_p)}; + } + + void advance_write_buffer(size_t size) { + this->file_->_p += size; + this->file_->_w -= size; + } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + return memchr(this->file_->_p + this->file_->_w, '\n', + to_unsigned(-this->file_->_w)); + } +}; + +// A fallback FILE wrapper. +template class fallback_file : public file_base { + private: + char next_; // The next unconsumed character in the buffer. + bool has_next_ = false; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { return false; } + auto needs_flush() const -> bool { return false; } + void init_buffer() {} + + auto get_read_buffer() const -> span { + return {&next_, has_next_ ? 1u : 0u}; + } + + auto get_write_buffer() const -> span { return {nullptr, 0}; } + + void advance_write_buffer(size_t) {} + + auto get() -> int { + has_next_ = false; + return file_base::get(); + } + + void unget(char c) { + file_base::unget(c); + next_ = c; + has_next_ = true; + } +}; + +#ifndef FMT_USE_FALLBACK_FILE +# define FMT_USE_FALLBACK_FILE 0 +#endif + +template +auto get_file(F* f, int) -> apple_file { + return f; +} +template +inline auto get_file(F* f, int) -> glibc_file { + return f; +} + +inline auto get_file(FILE* f, ...) -> fallback_file { return f; } + +using file_ref = decltype(get_file(static_cast(nullptr), 0)); + +template +class file_print_buffer : public buffer { + public: + explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} +}; + +template +class file_print_buffer::value>> + : public buffer { + private: + file_ref file_; + + static void grow(buffer& base, size_t) { + auto& self = static_cast(base); + self.file_.advance_write_buffer(self.size()); + if (self.file_.get_write_buffer().size == 0) self.file_.flush(); + auto buf = self.file_.get_write_buffer(); + FMT_ASSERT(buf.size > 0, ""); + self.set(buf.data, buf.size); + self.clear(); + } + + public: + explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { + flockfile(f); + file_.init_buffer(); + auto buf = file_.get_write_buffer(); + set(buf.data, buf.size); + } + ~file_print_buffer() { + file_.advance_write_buffer(size()); + bool flush = file_.needs_flush(); + F* f = file_; // Make funlockfile depend on the template parameter F + funlockfile(f); // for the system API detection to work. + if (flush) fflush(file_); + } +}; + +#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) +FMT_FUNC auto write_console(int, string_view) -> bool { return false; } +#else +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); + +FMT_FUNC bool write_console(int fd, string_view text) { + auto u16 = utf8_to_utf16(text); + return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), + static_cast(u16.size()), nullptr, nullptr) != 0; +} +#endif + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, + bool newline) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + if (newline) buffer.push_back('\n'); + fwrite_all(buffer.data(), buffer.size(), f); +} +#endif + +FMT_FUNC void print(std::FILE* f, string_view text) { +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) + int fd = _fileno(f); + if (_isatty(fd)) { + std::fflush(f); + if (write_console(fd, text)) return; + } +#endif + fwrite_all(text.data(), text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) + return vprint_buffered(f, fmt, args); + auto&& buffer = detail::file_print_buffer<>(f); + return detail::vformat_to(buffer, fmt, args); +} + +FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + buffer.push_back('\n'); + detail::print(f, {buffer.data(), buffer.size()}); +} + +FMT_FUNC void vprint(string_view fmt, format_args args) { + vprint(stdout, fmt, args); +} + +namespace detail { + +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// This code is generated by support/printable.py. +FMT_FUNC auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +} // namespace detail + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/deps/fmt/include/fmt/format.h b/deps/fmt/include/fmt/format.h new file mode 100644 index 00000000000..50e571442e5 --- /dev/null +++ b/deps/fmt/include/fmt/format.h @@ -0,0 +1,4244 @@ +/* + 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_ + +#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define FMT_REMOVE_TRANSITIVE_INCLUDES +#endif + +#include "base.h" + +#ifndef FMT_MODULE +# include // std::signbit +# include // std::byte +# include // uint32_t +# include // std::memcpy +# include // std::numeric_limits +# include // std::bad_alloc +# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) +// Workaround for pre gcc 5 libstdc++. +# include // std::allocator_traits +# endif +# include // std::runtime_error +# include // std::string +# include // std::system_error + +// Check FMT_CPLUSPLUS to avoid a warning in MSVC. +# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L +# include // std::bit_cast +# endif + +// libc++ supports string_view in pre-c++17. +# if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include +# define FMT_USE_STRING_VIEW +# endif + +# if FMT_MSC_VERSION +# include // _BitScanReverse[64], _umul128 +# endif +#endif // FMT_MODULE + +#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) +// Use the provided definition. +#elif defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif defined(__cpp_nontype_template_args) && \ + __cpp_nontype_template_args >= 201911L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#endif + +#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L +# define FMT_INLINE_VARIABLE inline +#else +# define FMT_INLINE_VARIABLE +#endif + +// Check if RTTI is disabled. +#ifdef FMT_USE_RTTI +// Use the provided definition. +#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ + defined(__INTEL_RTTI__) || defined(__RTTI) +// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. +# define FMT_USE_RTTI 1 +#else +# define FMT_USE_RTTI 0 +#endif + +// Visibility when compiled as a shared library/object. +#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value) +#else +# define FMT_SO_VISIBILITY(value) +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +// GCC 4.9 doesn't support qualified names in specializations. +namespace std { +template struct iterator_traits> { + using iterator_category = output_iterator_tag; + using value_type = T; + using difference_type = + decltype(static_cast(nullptr) - static_cast(nullptr)); + using pointer = void; + using reference = void; +}; +} // namespace std + +#ifndef FMT_THROW +# if FMT_USE_EXCEPTIONS +# if FMT_MSC_VERSION || defined(__NVCC__) +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) +# endif // FMT_USE_EXCEPTIONS +#endif // FMT_THROW + +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#endif + +FMT_BEGIN_NAMESPACE + +template +struct is_contiguous> + : std::true_type {}; + +namespace detail { + +// __builtin_clz is broken in clang with Microsoft codegen: +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VERSION +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif +#endif + +// Some compilers masquerade as both MSVC and GCC but 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_VERSION && !defined(FMT_BUILTIN_CLZLL) +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# ifdef _WIN64 +# pragma intrinsic(_BitScanReverse64) +# endif +# endif + +inline auto clz(uint32_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + unsigned long r = 0; + _BitScanReverse(&r, x); + return 31 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +inline auto clzll(uint64_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 ^ static_cast(r + 32); + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + return 63 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) +#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) + +FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { + ignore_unused(condition); +#ifdef FMT_FUZZ + if (condition) throw std::runtime_error("fuzzing limit reached"); +#endif +} + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#else +template struct std_string_view { + operator basic_string_view() const; +}; +#endif + +template struct string_literal { + static constexpr Char value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { + return {value, sizeof...(C)}; + } +}; +#if FMT_CPLUSPLUS < 201703L +template +constexpr Char string_literal::value[sizeof...(C)]; +#endif + +// Implementation of std::bit_cast for pre-C++20. +template +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast(from); +#endif + auto to = To(); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast(&to), &from, sizeof(to)); + return to; +} + +inline auto is_big_endian() -> bool { +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else + struct bytes { + char data[sizeof(int)]; + }; + return bit_cast(1).data[0] == 0; +#endif +} + +class uint128_fallback { + private: + uint64_t lo_, hi_; + + public: + constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} + constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} + + constexpr auto high() const noexcept -> uint64_t { return hi_; } + constexpr auto low() const noexcept -> uint64_t { return lo_; } + + template ::value)> + constexpr explicit operator T() const { + return static_cast(lo_); + } + + friend constexpr auto operator==(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; + } + friend constexpr auto operator!=(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return !(lhs == rhs); + } + friend constexpr auto operator>(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; + } + friend constexpr auto operator|(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; + } + friend constexpr auto operator&(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; + } + friend constexpr auto operator~(const uint128_fallback& n) + -> uint128_fallback { + return {~n.hi_, ~n.lo_}; + } + friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + auto result = uint128_fallback(lhs); + result += rhs; + return result; + } + friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs) + -> uint128_fallback { + FMT_ASSERT(lhs.hi_ == 0, ""); + uint64_t hi = (lhs.lo_ >> 32) * rhs; + uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; + uint64_t new_lo = (hi << 32) + lo; + return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; + } + friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs) + -> uint128_fallback { + return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; + } + FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { + if (shift == 64) return {0, hi_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); + return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; + } + FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { + return *this = *this >> shift; + } + FMT_CONSTEXPR void operator+=(uint128_fallback n) { + uint64_t new_lo = lo_ + n.lo_; + uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); + FMT_ASSERT(new_hi >= hi_, ""); + lo_ = new_lo; + hi_ = new_hi; + } + FMT_CONSTEXPR void operator&=(uint128_fallback n) { + lo_ &= n.lo_; + hi_ &= n.hi_; + } + + FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; + } +#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; + } +}; + +using uint128_t = conditional_t; + +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +#else +using uintptr_t = uint128_t; +#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 auto max_value() -> T { + return (std::numeric_limits::max)(); +} +template constexpr auto num_bits() -> int { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } + +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); + struct data_t { + unsigned short value[static_cast(size)]; + } data = bit_cast(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits()) | data.value[i]; + } + return result; +} + +template +FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int { + int lz = 0; + constexpr UInt msb_mask = static_cast(1) << (num_bits() - 1); + for (; (n & msb_mask) == 0; n <<= 1) lz++; + return lz; +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n); +#endif + return countl_zero_fallback(n); +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n); +#endif + return countl_zero_fallback(n); +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION + __builtin_assume(condition); +#elif FMT_GCC_VERSION + if (!condition) __builtin_unreachable(); +#endif +} + +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. +template ::value&& + is_contiguous::value)> +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +FMT_CONSTEXPR20 inline auto +reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { + auto& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return &c[size]; +} + +template +FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) + -> basic_appender { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} + +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { + return it; +} + +template +using reserve_iterator = + remove_reference_t(), 0))>; + +template +constexpr auto to_pointer(OutputIt, size_t) -> T* { + return nullptr; +} +template +FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} + +template ::value&& + is_contiguous::value)> +inline auto base_iterator(OutputIt it, + typename OutputIt::container_type::value_type*) + -> OutputIt { + return it; +} + +template +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { + return it; +} + +// is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { + if (is_constant_evaluated()) return fill_n(out, count, value); + std::memset(out, value, to_unsigned(count)); + return out + count; +} + +template +FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy(begin, end, out); +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { + constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr const int shiftc[] = {0, 18, 12, 6, 0}; + constexpr const int shifte[] = {0, 6, 4, 2, 0}; + + int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" + [static_cast(*s) >> 3]; + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = s + len + !len; + + using uchar = unsigned char; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. +template +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* buf_ptr, const char* ptr) { + auto cp = uint32_t(); + auto error = 0; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : end) : nullptr; + }; + + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) { + p = decode(p, p); + if (!p) return; + } + } + auto num_chars_left = to_unsigned(s.data() + s.size() - p); + if (num_chars_left == 0) return; + + // Suppress bogus -Wstringop-overflow. + if (FMT_GCC_VERSION) num_chars_left &= 3; + char buf[2 * block_size - 1] = {}; + copy(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr < buf + num_chars_left); +} + +template +inline auto compute_width(basic_string_view s) -> size_t { + return s.size(); +} + +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { + size_t num_code_points = 0; + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { + *count += to_unsigned( + 1 + + (cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; + } + }; + // We could avoid branches by using utf8_decode directly. + for_each_codepoint(s, count_code_points{&num_code_points}); + return num_code_points; +} + +template +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { + return min_of(n, s.size()); +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline auto code_point_index(string_view s, size_t n) -> size_t { + size_t result = s.size(); + const char* begin = s.begin(); + for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { + if (n != 0) { + --n; + return true; + } + result = to_unsigned(sv.begin() - begin); + return false; + }); + return result; +} + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +#if defined(FMT_USE_FLOAT128) +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ + !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +#else +# define FMT_USE_FLOAT128 0 +#endif +#if FMT_USE_FLOAT128 +using float128 = __float128; +#else +struct float128 {}; +#endif + +template using is_float128 = std::is_same; + +template struct is_floating_point : std::is_floating_point {}; +template <> struct is_floating_point : std::true_type {}; + +template ::value> +struct is_fast_float : bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template struct is_fast_float : std::false_type {}; + +template +using is_double_double = bool_constant::digits == 106>; + +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif + +// An allocator that uses malloc/free to allow removing dependency on the C++ +// standard libary runtime. +template struct allocator { + using value_type = T; + + T* allocate(size_t n) { + FMT_ASSERT(n <= max_value() / sizeof(T), ""); + T* p = static_cast(malloc(n * sizeof(T))); + if (!p) FMT_THROW(std::bad_alloc()); + return p; + } + + void deallocate(T* p, size_t) { free(p); } +}; + +} // namespace detail + +FMT_BEGIN_EXPORT + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. + */ +template > +class basic_memory_buffer : public detail::buffer { + private: + T store_[SIZE]; + + // Don't inherit from Allocator to avoid generating type_info for it. + FMT_NO_UNIQUE_ADDRESS Allocator alloc_; + + // Deallocate memory allocated by the buffer. + FMT_CONSTEXPR20 void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { + detail::abort_fuzzing_if(size > 5000); + auto& self = static_cast(buf); + const size_t max_size = + std::allocator_traits::max_size(self.alloc_); + size_t old_capacity = buf.capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = max_of(size, max_size); + T* old_data = buf.data(); + T* new_data = self.alloc_.allocate(new_capacity); + // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). + detail::assume(buf.size() <= new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + memcpy(new_data, old_data, buf.size() * sizeof(T)); + self.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 != self.store_) self.alloc_.deallocate(old_data, old_capacity); + } + + public: + using value_type = T; + using const_reference = const T&; + + FMT_CONSTEXPR explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) + : detail::buffer(grow), alloc_(alloc) { + this->set(store_, SIZE); + if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); + } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + detail::copy(other.store_, other.store_ + size, store_); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + other.clear(); + } + this->resize(size); + } + + public: + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept + : detail::buffer(grow) { + move(other); + } + + /// Moves the content of the other `basic_memory_buffer` object to this one. + auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + auto get_allocator() const -> Allocator { return alloc_; } + + /// Resizes the buffer to contain `count` elements. If T is a POD type new + /// elements may not be initialized. + FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } + + /// Increases the buffer capacity to `new_capacity`. + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + using detail::buffer::append; + template + FMT_CONSTEXPR20 void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } +}; + +using memory_buffer = basic_memory_buffer; + +template +FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) + -> std::string { + auto size = buf.size(); + detail::assume(size < std::string().max_size()); + return {buf.data(), size}; +} + +// A writer to a buffered stream. It doesn't own the underlying stream. +class writer { + private: + detail::buffer* buf_; + + // We cannot create a file buffer in advance because any write to a FILE may + // invalidate it. + FILE* file_; + + public: + inline writer(FILE* f) : buf_(nullptr), file_(f) {} + inline writer(detail::buffer& buf) : buf_(&buf) {} + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + if (buf_) + fmt::format_to(appender(*buf_), fmt, std::forward(args)...); + else + fmt::print(file_, fmt, std::forward(args)...); + } +}; + +class string_buffer { + private: + std::string str_; + detail::container_buffer buf_; + + public: + inline string_buffer() : buf_(str_) {} + + inline operator writer() { return buf_; } + inline std::string& str() { return str_; } +}; + +template +struct is_contiguous> : std::true_type { +}; + +// Suppress a misleading warning in older versions of clang. +FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") + +/// An error reported from a formatting function. +class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +class loc_value; + +FMT_END_EXPORT +namespace detail { +FMT_API auto write_console(int fd, string_view text) -> bool; +FMT_API void print(FILE*, string_view); +} // namespace detail + +namespace detail { +template struct fixed_string { + FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) { + detail::copy(static_cast(s), s + N, + data); + } + Char data[N] = {}; +}; + +// Converts a compile-time string to basic_string_view. +FMT_EXPORT template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +FMT_EXPORT template +constexpr auto compile_string_to_view(basic_string_view s) + -> basic_string_view { + return s; +} + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +constexpr auto is_negative(T value) -> bool { + return value < 0; +} +template ::value)> +constexpr auto is_negative(T) -> bool { + return false; +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; +template +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ + (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ + (factor) * 100000000, (factor) * 1000000000 + +// Converts value in the range [0, 100) to a string. +// GCC generates slightly better code when value is pointer-size. +inline auto digits2(size_t value) -> const char* { + // Align data since unaligned access may be slower when crossing a + // hardware-specific boundary. + alignas(2) static const char data[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + return &data[value * 2]; +} + +template constexpr auto getsign(sign s) -> Char { + return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> + (static_cast(s) * 8)); +} + +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { + 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; + } +} +#if FMT_USE_INT128 +FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { + return count_digits_fallback(n); +} +#endif + +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); +} +#endif + +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); +#endif + return count_digits_fallback(n); +} + +// Counts the number of digits in n. BITS = log2(radix). +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated() && num_bits() == 32) + return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; +#endif + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); +} + +#ifdef FMT_BUILTIN_CLZ +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast((n + inc) >> 32); +} +#endif + +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); +#endif + return count_digits_fallback(n); +} + +template constexpr auto digits10() noexcept -> int { + return std::numeric_limits::digits10; +} +template <> constexpr auto digits10() noexcept -> int { return 38; } +template <> constexpr auto digits10() noexcept -> int { return 38; } + +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; +} +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + return thousands_sep_impl(loc); +} + +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { + return Char(decimal_point_impl(loc)); +} +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { + return decimal_point_impl(loc); +} + +#ifndef FMT_HEADER_ONLY +FMT_BEGIN_EXPORT +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +FMT_END_EXPORT +#endif // FMT_HEADER_ONLY + +// Compares two characters for equality. +template auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); +} +inline auto equal2(const char* lhs, const char* rhs) -> bool { + return memcmp(lhs, rhs, 2) == 0; +} + +// Writes a two-digit value to out. +template +FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { + if (!is_constant_evaluated() && std::is_same::value && + !FMT_OPTIMIZE_SIZE) { + memcpy(out, digits2(value), 2); + return; + } + *out++ = static_cast('0' + value / 10); + *out = static_cast('0' + value % 10); +} + +// Formats a decimal unsigned integer value writing to out pointing to a buffer +// of specified size. The caller must ensure that the buffer is large enough. +template +FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) + -> Char* { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + unsigned n = to_unsigned(size); + 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. + n -= 2; + write2digits(out + n, static_cast(value % 100)); + value /= 100; + } + if (value >= 10) { + n -= 2; + write2digits(out + n, static_cast(value)); + } else { + out[--n] = static_cast('0' + value); + } + return out + n; +} + +template +FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, + int num_digits) -> Char* { + do_format_decimal(out, value, num_digits); + return out + num_digits; +} + +template >::value)> +FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + do_format_decimal(ptr, value, num_digits); + return out; + } + // Buffer is large enough to hold all digits (digits10 + 1). + char buffer[digits10() + 1]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + do_format_decimal(buffer, value, num_digits); + return copy_noinline(buffer, buffer + num_digits, out); +} + +template +FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, + int size, bool upper = false) -> Char* { + out += size; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= base_bits) != 0); + return out; +} + +// Formats an unsigned integer in the power of two base (binary, octal, hex). +template +FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, + int num_digits, bool upper = false) -> Char* { + do_format_base2e(base_bits, out, value, num_digits, upper); + return out + num_digits; +} + +template ::value)> +FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, + int num_digits, bool upper = false) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_base2e(base_bits, ptr, value, num_digits, upper); + return out; + } + // Make buffer large enough for any base. + char buffer[num_bits()]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + format_base2e(base_bits, buffer, value, num_digits, upper); + return detail::copy_noinline(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + basic_memory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + inline operator basic_string_view() const { + return {&buffer_[0], size()}; + } + inline auto size() const -> size_t { return buffer_.size() - 1; } + inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } + inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } +}; + +enum class to_utf8_error_policy { abort, replace }; + +// A converter from UTF-16/UTF-32 (host endian) to UTF-8. +template class to_utf8 { + private: + Buffer buffer_; + + public: + to_utf8() {} + explicit to_utf8(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) { + static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, + "Expect utf16 or utf32"); + if (!convert(s, policy)) + FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" + : "invalid utf32")); + } + operator string_view() const { return string_view(&buffer_[0], size()); } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const char* { return &buffer_[0]; } + auto str() const -> std::string { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a bool instead of throwing exception on + // conversion error. This method may still throw in case of memory allocation + // error. + auto convert(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { + if (!convert(buffer_, s, policy)) return false; + buffer_.push_back(0); + return true; + } + static auto convert(Buffer& buf, basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { + for (auto p = s.begin(); p != s.end(); ++p) { + uint32_t c = static_cast(*p); + if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { + // Handle a surrogate pair. + ++p; + if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + if (policy == to_utf8_error_policy::abort) return false; + buf.append(string_view("\xEF\xBF\xBD")); + --p; + continue; + } else { + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + return false; + } + } + return true; + } +}; + +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return {static_cast(p >> 64), static_cast(p)}; +#elif defined(_MSC_VER) && defined(_M_X64) + auto hi = uint64_t(); + auto lo = _umul128(x, y, &hi); + return {hi, lo}; +#else + const uint64_t mask = static_cast(max_value()); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +namespace dragonbox { +// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from +// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. +inline auto floor_log10_pow2(int e) noexcept -> int { + FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); + static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); + return (e * 315653) >> 20; +} + +inline auto floor_log2_pow10(int e) noexcept -> int { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} + +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { + uint128_fallback r = umul128(x, y.high()); + r += umul128_upper64(x, y.low()); + return r; +} + +FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; + +// Type-specific information that Dragonbox uses. +template struct float_info; + +template <> struct float_info { + using carrier_uint = uint32_t; + static const int exponent_bits = 8; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; +}; + +template <> struct float_info { + using carrier_uint = uint64_t; + static const int exponent_bits = 11; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 341; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; +}; + +// An 80- or 128-bit floating point number. +template +struct float_info::digits == 64 || + std::numeric_limits::digits == 113 || + is_float128::value>> { + using carrier_uint = detail::uint128_t; + static const int exponent_bits = 15; +}; + +// A double-double floating point number. +template +struct float_info::value>> { + using carrier_uint = detail::uint128_t; +}; + +template struct decimal_fp { + using significand_type = typename float_info::carrier_uint; + significand_type significand; + int exponent; +}; + +template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; +} // namespace dragonbox + +// Returns true iff Float has the implicit bit which is not stored. +template constexpr auto has_implicit_bit() -> bool { + // An 80-bit FP number has a 64-bit significand an no implicit bit. + return std::numeric_limits::digits != 64; +} + +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. +template constexpr auto num_significand_bits() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 112 + : (std::numeric_limits::digits - + (has_implicit_bit() ? 1 : 0)); +} + +template +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { + using float_uint = typename dragonbox::float_info::carrier_uint; + return ((float_uint(1) << dragonbox::float_info::exponent_bits) - 1) + << num_significand_bits(); +} +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; +} + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template +FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *out++ = static_cast('-'); + exp = -exp; + } else { + *out++ = static_cast('+'); + } + auto uexp = static_cast(exp); + if (is_constant_evaluated()) { + if (uexp < 10) *out++ = '0'; + return format_decimal(out, uexp, count_digits(uexp)); + } + if (uexp >= 100u) { + const char* top = digits2(uexp / 100); + if (uexp >= 1000u) *out++ = static_cast(top[0]); + *out++ = static_cast(top[1]); + uexp %= 100; + } + const char* d = digits2(uexp); + *out++ = static_cast(d[0]); + *out++ = static_cast(d[1]); + return out; +} + +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; + + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); + } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + const auto shifted_implicit_bit = 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 = basic_fp::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { +#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 +} + +FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template () == num_bits()> +using convert_float_result = + conditional_t::value || doublish, double, T>; + +template +constexpr auto convert_float(T value) -> convert_float_result { + return static_cast>(value); +} + +template +FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, + const basic_specs& specs) -> OutputIt { + auto fill_size = specs.fill_size(); + if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); + if (const Char* data = specs.fill()) { + for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); + } + return it; +} + +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { + static_assert(default_align == align::left || default_align == align::right, + ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = + default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[static_cast(specs.align())]; + size_t right_padding = padding - left_padding; + auto it = reserve(out, size + padding * specs.fill_size()); + if (left_padding != 0) it = fill(it, left_padding, specs); + it = f(it); + if (right_padding != 0) it = fill(it, right_padding, specs); + return base_iterator(out, it); +} + +template +constexpr auto write_padded(OutputIt out, const format_specs& specs, + size_t size, F&& f) -> OutputIt { + return write_padded(out, specs, size, size, f); +} + +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const format_specs& specs = {}) -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy(data, data + bytes.size(), it); + }); +} + +template +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) + -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_base2e(4, it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +// Returns true iff the code point cp is printable. +FMT_API auto is_printable(uint32_t cp) -> bool; + +inline auto needs_escape(uint32_t cp) -> bool { + if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; + if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false; + return !is_printable(cp); +} + +template struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + uint32_t cp = static_cast>(*begin); + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result { + if (const_check(!use_utf8)) return find_escape(begin, end); + auto result = find_escape_result{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_base2e(4, buf, cp, width); + return copy(buf, buf + width, out); +} + +template +auto write_escaped_cp(OutputIt out, const find_escape_result& escape) + -> OutputIt { + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = static_cast('\\'); + c = static_cast('n'); + break; + case '\r': + *out++ = static_cast('\\'); + c = static_cast('r'); + break; + case '\t': + *out++ = static_cast('\\'); + c = static_cast('t'); + break; + case '"': FMT_FALLTHROUGH; + case '\'': FMT_FALLTHROUGH; + case '\\': *out++ = static_cast('\\'); break; + default: + if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); + if (escape.cp < 0x10000) + return write_codepoint<4, Char>(out, 'u', escape.cp); + if (escape.cp < 0x110000) + return write_codepoint<8, Char>(out, 'U', escape.cp); + for (Char escape_char : basic_string_view( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); + } + return out; + } + *out++ = c; + return out; +} + +template +auto write_escaped_string(OutputIt out, basic_string_view str) + -> OutputIt { + *out++ = static_cast('"'); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + out = write_escaped_cp(out, escape); + } while (begin != end); + *out++ = static_cast('"'); + return out; +} + +template +auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + Char v_array[1] = {v}; + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { + out = write_escaped_cp(out, + find_escape_result{v_array, v_array + 1, + static_cast(v)}); + } else { + *out++ = v; + } + *out++ = static_cast('\''); + return out; +} + +template +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { + if (is_debug) return write_escaped_char(it, value); + *it++ = value; + return it; + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} + +template class digit_grouping { + private: + std::string grouping_; + std::basic_string thousands_sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } + + // Returns the next digit group separator position. + auto next(next_state& state) const -> int { + if (thousands_sep_.empty()) return max_value(); + if (state.group == grouping_.end()) return state.pos += grouping_.back(); + if (*state.group <= 0 || *state.group == max_value()) + return max_value(); + state.pos += *state.group++; + return state.pos; + } + + public: + template ::value)> + explicit digit_grouping(Locale loc, bool localized = true) { + if (!localized) return; + auto sep = thousands_sep(loc); + grouping_ = sep.grouping; + if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); + } + digit_grouping(std::string grouping, std::basic_string sep) + : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} + + auto has_separator() const -> bool { return !thousands_sep_.empty(); } + + auto count_separators(int num_digits) const -> int { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template + auto apply(Out out, basic_string_view digits) const -> Out { + auto num_digits = static_cast(digits.size()); + auto separators = basic_memory_buffer(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + out = copy(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +// Writes a decimal integer with digit grouping. +template +auto write_int(OutputIt out, UInt value, unsigned prefix, + const format_specs& specs, const digit_grouping& grouping) + -> OutputIt { + static_assert(std::is_same, UInt>::value, ""); + int num_digits = 0; + auto buffer = memory_buffer(); + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; + case presentation_type::none: + case presentation_type::dec: + num_digits = count_digits(value); + format_decimal(appender(buffer), value, num_digits); + break; + case presentation_type::hex: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + num_digits = count_digits<4>(value); + format_base2e(4, appender(buffer), value, num_digits, specs.upper()); + break; + case presentation_type::oct: + num_digits = count_digits<3>(value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt() && specs.precision <= num_digits && value != 0) + prefix_append(prefix, '0'); + format_base2e(3, appender(buffer), value, num_digits); + break; + case presentation_type::bin: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_base2e(1, appender(buffer), value, num_digits); + break; + case presentation_type::chr: + return write_char(out, static_cast(value), specs); + } + + unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + + to_unsigned(grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return grouping.apply(it, string_view(buffer.data(), buffer.size())); + }); +} + +#if FMT_USE_LOCALE +// Writes a localized value. +FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, + locale_ref loc) -> bool; +#endif +template +inline auto write_loc(OutputIt, const loc_value&, const format_specs&, + locale_ref) -> bool { + return false; +} + +template struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template +FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) + -> write_int_arg> { + auto prefix = 0u; + auto abs_value = static_cast>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[static_cast(s)]; + } + return {abs_value, prefix}; +} + +template struct loc_writer { + basic_appender out; + const format_specs& specs; + std::basic_string sep; + std::string grouping; + std::basic_string decimal_point; + + template ::value)> + auto operator()(T value) -> bool { + auto arg = make_write_int_arg(value, specs.sign()); + write_int(out, static_cast>(arg.abs_value), arg.prefix, + specs, digit_grouping(grouping, sep)); + return true; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + +// Size and padding computation separate from write_int to avoid template bloat. +struct size_padding { + unsigned size; + unsigned padding; + + FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, + const format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align() == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const format_specs& specs) -> OutputIt { + static_assert(std::is_same>::value, ""); + + constexpr int buffer_size = num_bits(); + char buffer[buffer_size]; + if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); + const char* begin = nullptr; + const char* end = buffer + buffer_size; + + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; + case presentation_type::none: + case presentation_type::dec: + begin = do_format_decimal(buffer, abs_value, buffer_size); + break; + case presentation_type::hex: + begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + break; + case presentation_type::oct: { + begin = do_format_base2e(3, buffer, abs_value, buffer_size); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + auto num_digits = end - begin; + if (specs.alt() && specs.precision <= num_digits && abs_value != 0) + prefix_append(prefix, '0'); + break; + } + case presentation_type::bin: + begin = do_format_base2e(1, buffer, abs_value, buffer_size); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + break; + case presentation_type::chr: + return write_char(out, static_cast(abs_value), specs); + } + + // Write an integer in the format + // + // prefix contains chars in three lower bytes and the size in the fourth byte. + int num_digits = static_cast(end - begin); + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return base_iterator(out, copy(begin, end, it)); + } + auto sp = size_padding(num_digits, prefix, specs); + unsigned padding = sp.padding; + return write_padded( + out, specs, sp.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, padding, static_cast('0')); + return copy(begin, end, it); + }); +} + +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, + write_int_arg arg, + const format_specs& specs) + -> OutputIt { + return write_int(out, arg, specs); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, + const format_specs& specs, locale_ref loc) + -> basic_appender { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int_noinline(out, make_write_int_arg(value, specs.sign()), + specs); +} + +// An inlined version of write used in format string compilation. +template ::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const format_specs& specs, locale_ref loc) + -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int(out, make_write_int_arg(value, specs.sign()), specs); +} + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs) -> OutputIt { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + + bool is_debug = specs.type() == presentation_type::debug; + if (is_debug) { + auto buf = counting_buffer(); + write_escaped_string(basic_appender(buf), s); + size = buf.count(); + } + + size_t width = 0; + if (specs.width != 0) { + width = + is_debug ? size : compute_width(basic_string_view(data, size)); + } + return write_padded( + out, specs, size, width, [=](reserve_iterator it) { + return is_debug ? write_escaped_string(it, s) + : copy(data, data + size, it); + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs, locale_ref) -> OutputIt { + return write(out, s, specs); +} +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, + locale_ref) -> OutputIt { + if (specs.type() == presentation_type::pointer) + return write_ptr(out, bit_cast(s), &specs); + if (!s) report_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + 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 size = (negative ? 1 : 0) + static_cast(num_digits); + if (auto ptr = to_pointer(out, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *out++ = static_cast('-'); + return format_decimal(out, abs_value, num_digits); +} + +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + format_specs& specs) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto alignment = align::none; + auto p = begin + code_point_length(begin); + if (end - p <= 0) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': alignment = align::left; break; + case '>': alignment = align::right; break; + case '^': alignment = align::center; break; + } + if (alignment != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '}') return begin; + if (c == '{') { + report_error("invalid fill character '{'"); + return begin; + } + specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); + begin = p + 1; + } else { + ++begin; + } + break; + } else if (p == begin) { + break; + } + p = begin; + } + specs.set_align(alignment); + return begin; +} + +template +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, + format_specs specs, sign s) -> OutputIt { + auto str = + isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); + constexpr size_t str_size = 3; + auto size = str_size + (s != sign::none ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill_size() == 1 && specs.fill_unit() == '0'; + if (is_zero_fill) specs.set_fill(' '); + return write_padded(out, specs, size, + [=](reserve_iterator it) { + if (s != sign::none) + *it++ = detail::getsign(s); + return copy(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; +} +template +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); +} + +template +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { + return copy(significand, significand + significand_size, out); +} +template +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { + return format_decimal(out, significand, significand_size); +} +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.has_separator()) { + out = write_significand(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast('0')); + } + auto buffer = memory_buffer(); + write_significand(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} + +template ::value)> +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { + if (!decimal_point) return format_decimal(out, significand, significand_size); + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + write2digits(out, static_cast(significand % 100)); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast('0' + significand % 10); + significand /= 10; + } + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); + return end; +} + +template >::value)> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_noinline(buffer, end, out); +} + +template +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_noinline(significand, significand + integral_size, + out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_noinline(significand + integral_size, + significand + significand_size, out); +} + +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.has_separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer(); + write_significand(basic_appender(buffer), significand, significand_size, + integral_size, decimal_point); + grouping.apply( + out, basic_string_view(buffer.data(), to_unsigned(integral_size))); + return detail::copy_noinline(buffer.data() + integral_size, + buffer.end(), out); +} + +template > +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast('0'); + size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); + using iterator = reserve_iterator; + + Char decimal_point = specs.localized() ? detail::decimal_point(loc) + : static_cast('.'); + + int output_exp = f.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (specs.type() == presentation_type::exp) return true; + if (specs.type() == presentation_type::fixed) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4; + return output_exp < exp_lower || + output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (specs.alt()) { + num_zeros = specs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = specs.upper() ? 'E' : 'e'; + auto write = [=](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }; + return specs.width > 0 + ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(f.exponent); + int num_zeros = specs.precision - exp; + abort_fuzzing_if(num_zeros > 5000); + if (specs.alt()) { + ++size; + if (num_zeros <= 0 && specs.type() != presentation_type::fixed) + num_zeros = 0; + if (num_zeros > 0) size += to_unsigned(num_zeros); + } + auto grouping = Grouping(loc, specs.localized()); + size += to_unsigned(grouping.count_separators(exp)); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, significand, significand_size, + f.exponent, grouping); + if (!specs.alt()) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = specs.alt() ? specs.precision - significand_size : 0; + size += 1 + static_cast(max_of(num_zeros, 0)); + auto grouping = Grouping(loc, specs.localized()); + size += to_unsigned(grouping.count_separators(exp)); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { + num_zeros = specs.precision; + } + bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + *it++ = zero; + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, zero); + return write_significand(it, significand, significand_size); + }); +} + +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr auto has_separator() const -> bool { return false; } + + constexpr auto count_separators(int) const -> int { return 0; } + + template + constexpr auto apply(Out out, basic_string_view) const -> Out { + return out; + } +}; + +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float>(out, f, specs, s, + exp_upper, loc); + } else { + return do_write_float(out, f, specs, s, exp_upper, loc); + } +} + +template constexpr auto isnan(T value) -> bool { + return value != value; // std::isnan doesn't support __float128. +} + +template +struct has_isfinite : std::false_type {}; + +template +struct has_isfinite> + : std::true_type {}; + +template ::value&& has_isfinite::value)> +FMT_CONSTEXPR20 auto isfinite(T value) -> bool { + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value < inf && value > -inf; + return std::isfinite(value); +} +template ::value)> +FMT_CONSTEXPR auto isfinite(T value) -> bool { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value < inf && value > -inf; +} + +template ::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits >> (num_bits() - 1)) != 0; + } +#endif + } + return std::signbit(static_cast(value)); +} + +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +class bigint { + private: + // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. + using bigit = uint32_t; // A big digit. + using double_bigit = uint64_t; + enum { bigit_bits = num_bits() }; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + friend struct formatter; + + FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { + return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; + } + + FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = double_bigit(bigits_[index]) - other - borrow; + bigits_[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + FMT_CONSTEXPR 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 (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + if (borrow != 0) subtract_bigits(i, 0, borrow); + FMT_ASSERT(borrow == 0, ""); + remove_leading_zeros(); + } + + FMT_CONSTEXPR void multiply(uint32_t value) { + bigit carry = 0; + const double_bigit wide_value = value; + 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); + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR void multiply(UInt value) { + using half_uint = + conditional_t::value, uint64_t, uint32_t>; + const int shift = num_bits() - bigit_bits; + const UInt lower = static_cast(value); + const UInt upper = value >> num_bits(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(static_cast(carry)); + carry >>= bigit_bits; + } + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(n); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + public: + FMT_CONSTEXPR bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + copy(data, data + size, bigits_.data()); + exp_ = other.exp_; + } + + template FMT_CONSTEXPR void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(n)); + } + + FMT_CONSTEXPR auto num_bigits() const -> int { + return static_cast(bigits_.size()) + exp_; + } + + FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { + FMT_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 FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { + int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); + if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; + int i = static_cast(b1.bigits_.size()) - 1; + int j = static_cast(b2.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; + if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) -> int { + int max_lhs_bigits = max_of(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; + double_bigit borrow = 0; + int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); + bigit rhs_bigit = rhs.get_bigit(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. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return *this = 1; + int bitmask = 1 << (num_bits() - + countl_zero(static_cast(exp)) - 1); + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + *this = 5; + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + auto sum = uint128_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 += double_bigit(n[i]) * n[j]; + } + bigits_[bigit_index] = static_cast(sum); + sum >>= num_bits(); // 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 += double_bigit(n[i++]) * n[j--]; + bigits_[bigit_index] = static_cast(sum); + sum >>= num_bits(); + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +// format_dragon flags. +enum dragon { + predecessor_closer = 1, + fixup = 2, // Run fixup to correct exp10 which can be off by one. + fixed = 4, +}; + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, + unsigned flags, int num_digits, + 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). + // 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. + bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; + int shift = is_predecessor_closer ? 2 : 1; + if (value.e >= 0) { + numerator = value.f; + numerator <<= value.e + shift; + lower = 1; + lower <<= value.e; + if (is_predecessor_closer) { + upper_store = 1; + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (is_predecessor_closer) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= value.f; + numerator <<= shift; + denominator = 1; + denominator <<= shift - value.e; + } else { + numerator = value.f; + numerator <<= shift; + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower = 1; + if (is_predecessor_closer) { + upper_store = 1ULL << 1; + upper = &upper_store; + } + } + int even = static_cast((value.f & 1) == 0); + if (!upper) upper = &lower; + bool shortest = num_digits < 0; + if ((flags & dragon::fixup) != 0) { + if (add_compare(numerator, *upper, denominator) + even <= 0) { + --exp10; + numerator *= 10; + if (num_digits < 0) { + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (shortest) { + // Generate the shortest representation. + 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.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits <= 0) { + auto digit = '0'; + if (num_digits == 0) { + denominator *= 10; + digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + } + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + if ((flags & dragon::fixed) != 0) + buf.push_back('0'); + else + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +// Formats a floating-point number using the hexfloat format. +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { + // float is passed as double to reduce the number of instantiations and to + // simplify implementation. + static_assert(!std::is_same::value, ""); + + using info = dragonbox::float_info; + + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename info::carrier_uint; + + const auto num_float_significand_bits = detail::num_significand_bits(); + + basic_fp f(value); + f.e += num_float_significand_bits; + if (!has_implicit_bit()) --f.e; + + const auto num_fraction_bits = + num_float_significand_bits + (has_implicit_bit() ? 1 : 0); + const auto num_xdigits = (num_fraction_bits + 3) / 4; + + const auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_mask = carrier_uint(0xF) << leading_shift; + const auto leading_xdigit = + static_cast((f.f & leading_mask) >> leading_shift); + if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); + + int print_xdigits = num_xdigits - 1; + if (specs.precision >= 0 && print_xdigits > specs.precision) { + const int shift = ((print_xdigits - specs.precision - 1) * 4); + const auto mask = carrier_uint(0xF) << shift; + const auto v = static_cast((f.f & mask) >> shift); + + if (v >= 8) { + const auto inc = carrier_uint(1) << (shift + 4); + f.f += inc; + f.f &= ~(inc - 1); + } + + // Check long double overflow + if (!has_implicit_bit()) { + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + if ((f.f & implicit_bit) == implicit_bit) { + f.f >>= 4; + f.e += 4; + } + } + + print_xdigits = specs.precision; + } + + char xdigits[num_bits() / 4]; + detail::fill_n(xdigits, sizeof(xdigits), '0'); + format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); + + // Remove zero tail + while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; + + buf.push_back('0'); + buf.push_back(specs.upper() ? 'X' : 'x'); + buf.push_back(xdigits[0]); + if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) + buf.push_back('.'); + buf.append(xdigits + 1, xdigits + 1 + print_xdigits); + for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); + + buf.push_back(specs.upper() ? 'P' : 'p'); + + uint32_t abs_e; + if (f.e < 0) { + buf.push_back('-'); + abs_e = static_cast(-f.e); + } else { + buf.push_back('+'); + abs_e = static_cast(f.e); + } + format_decimal(appender(buf), abs_e, detail::count_digits(abs_e)); +} + +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { + format_hexfloat(static_cast(value), specs, buf); +} + +constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + // It is equal to ceil(2^31 + 2^32/10^(k + 1)). + // These are stored in a string literal because we cannot have static arrays + // in constexpr functions and non-static ones are poorly optimized. + return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7" + U"\x800001ae\x8000002b"[index]; +} + +template +FMT_CONSTEXPR20 auto format_float(Float value, int precision, + const format_specs& specs, bool binary32, + buffer& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); + auto converted_value = convert_float(value); + + const bool fixed = specs.type() == presentation_type::fixed; + if (value == 0) { + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + int exp = 0; + bool use_dragon = true; + unsigned dragon_flags = 0; + if (!is_fast_float() || is_constant_evaluated()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info; + const auto f = basic_fp(converted_value); + // Compute exp, an approximate power of 10, such that + // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). + // This is based on log10(value) == log2(value) / log2(10) and approximation + // of log2(value) by e + num_fraction_bits idea from double-conversion. + auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10; + exp = static_cast(e); + if (e > exp) ++exp; // Compute ceil. + dragon_flags = dragon::fixup; + } else { + // Extract significand bits and exponent bits. + using info = dragonbox::float_info; + auto br = bit_cast(static_cast(value)); + + const uint64_t significand_mask = + (static_cast(1) << num_significand_bits()) - 1; + uint64_t significand = (br & significand_mask); + int exponent = static_cast((br & exponent_mask()) >> + num_significand_bits()); + + if (exponent != 0) { // Check if normal. + exponent -= exponent_bias() + num_significand_bits(); + significand |= + (static_cast(1) << num_significand_bits()); + significand <<= 1; + } else { + // Normalize subnormal inputs. + FMT_ASSERT(significand != 0, "zeros should not appear here"); + int shift = countl_zero(significand); + FMT_ASSERT(shift >= num_bits() - num_significand_bits(), + ""); + shift -= (num_bits() - num_significand_bits() - 2); + exponent = (std::numeric_limits::min_exponent - + num_significand_bits()) - + shift; + significand <<= shift; + } + + // Compute the first several nonzero decimal significand digits. + // We call the number we get the first segment. + const int k = info::kappa - dragonbox::floor_log10_pow2(exponent); + exp = -k; + const int beta = exponent + dragonbox::floor_log2_pow10(k); + uint64_t first_segment; + bool has_more_segments; + int digits_in_the_first_segment; + { + const auto r = dragonbox::umul192_upper128( + significand << beta, dragonbox::get_cached_power(k)); + first_segment = r.high(); + has_more_segments = r.low() != 0; + + // The first segment can have 18 ~ 19 digits. + if (first_segment >= 1000000000000000000ULL) { + digits_in_the_first_segment = 19; + } else { + // When it is of 18-digits, we align it to 19-digits by adding a bogus + // zero at the end. + digits_in_the_first_segment = 18; + first_segment *= 10; + } + } + + // Compute the actual number of decimal digits to print. + if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment); + + // Use Dragon4 only when there might be not enough digits in the first + // segment. + if (digits_in_the_first_segment > precision) { + use_dragon = false; + + if (precision <= 0) { + exp += digits_in_the_first_segment; + + if (precision < 0) { + // Nothing to do, since all we have are just leading zeros. + buf.try_resize(0); + } else { + // We may need to round-up. + buf.try_resize(1); + if ((first_segment | static_cast(has_more_segments)) > + 5000000000000000000ULL) { + buf[0] = '1'; + } else { + buf[0] = '0'; + } + } + } // precision <= 0 + else { + exp += digits_in_the_first_segment - precision; + + // When precision > 0, we divide the first segment into three + // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits + // in 32-bits which usually allows faster calculation than in + // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize + // division-by-constant for large 64-bit divisors, we do it here + // manually. The magic number 7922816251426433760 below is equal to + // ceil(2^(64+32) / 10^10). + const uint32_t first_subsegment = static_cast( + dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >> + 32); + const uint64_t second_third_subsegments = + first_segment - first_subsegment * 10000000000ULL; + + uint64_t prod; + uint32_t digits; + bool should_round_up; + int number_of_digits_to_print = min_of(precision, 9); + + // Print a 9-digits subsegment, either the first or the second. + auto print_subsegment = [&](uint32_t subsegment, char* buffer) { + int number_of_digits_printed = 0; + + // If we want to print an odd number of digits from the subsegment, + if ((number_of_digits_to_print & 1) != 0) { + // Convert to 64-bit fixed-point fractional form with 1-digit + // integer part. The magic number 720575941 is a good enough + // approximation of 2^(32 + 24) / 10^8; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(720575941)) >> 24) + 1; + digits = static_cast(prod >> 32); + *buffer = static_cast('0' + digits); + number_of_digits_printed++; + } + // If we want to print an even number of digits from the + // first_subsegment, + else { + // Convert to 64-bit fixed-point fractional form with 2-digits + // integer part. The magic number 450359963 is a good enough + // approximation of 2^(32 + 20) / 10^7; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(450359963)) >> 20) + 1; + digits = static_cast(prod >> 32); + write2digits(buffer, digits); + number_of_digits_printed += 2; + } + + // Print all digit pairs. + while (number_of_digits_printed < number_of_digits_to_print) { + prod = static_cast(prod) * static_cast(100); + digits = static_cast(prod >> 32); + write2digits(buffer + number_of_digits_printed, digits); + number_of_digits_printed += 2; + } + }; + + // Print first subsegment. + print_subsegment(first_subsegment, buf.data()); + + // Perform rounding if the first subsegment is the last subsegment to + // print. + if (precision <= 9) { + // Rounding inside the subsegment. + // We round-up if: + // - either the fractional part is strictly larger than 1/2, or + // - the fractional part is exactly 1/2 and the last digit is odd. + // We rely on the following observations: + // - If fractional_part >= threshold, then the fractional part is + // strictly larger than 1/2. + // - If the MSB of fractional_part is set, then the fractional part + // must be at least 1/2. + // - When the MSB of fractional_part is set, either + // second_third_subsegments being nonzero or has_more_segments + // being true means there are further digits not printed, so the + // fractional part is strictly larger than 1/2. + if (precision < 9) { + uint32_t fractional_part = static_cast(prod); + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (second_third_subsegments != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + // In this case, the fractional part is at least 1/2 if and only if + // second_third_subsegments >= 5000000000ULL, and is strictly larger + // than 1/2 if we further have either second_third_subsegments > + // 5000000000ULL or has_more_segments == true. + else { + should_round_up = second_third_subsegments > 5000000000ULL || + (second_third_subsegments == 5000000000ULL && + ((digits & 1) != 0 || has_more_segments)); + } + } + // Otherwise, print the second subsegment. + else { + // Compilers are not aware of how to leverage the maximum value of + // second_third_subsegments to find out a better magic number which + // allows us to eliminate an additional shift. 1844674407370955162 = + // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))). + const uint32_t second_subsegment = + static_cast(dragonbox::umul128_upper64( + second_third_subsegments, 1844674407370955162ULL)); + const uint32_t third_subsegment = + static_cast(second_third_subsegments) - + second_subsegment * 10; + + number_of_digits_to_print = precision - 9; + print_subsegment(second_subsegment, buf.data() + 9); + + // Rounding inside the subsegment. + if (precision < 18) { + // The condition third_subsegment != 0 implies that the segment was + // of 19 digits, so in this case the third segment should be + // consisting of a genuine digit from the input. + uint32_t fractional_part = static_cast(prod); + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (third_subsegment != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + else { + // In this case, the segment must be of 19 digits, thus + // the third subsegment should be consisting of a genuine digit from + // the input. + should_round_up = third_subsegment > 5 || + (third_subsegment == 5 && + ((digits & 1) != 0 || has_more_segments)); + } + } + + // Round-up if necessary. + if (should_round_up) { + ++buf[precision - 1]; + for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[precision++] = '0'; + else + ++exp; + } + } + buf.try_resize(to_unsigned(precision)); + } + } // if (digits_in_the_first_segment > precision) + else { + // Adjust the exponent for its use in Dragon4. + exp += digits_in_the_first_segment - 1; + } + } + if (use_dragon) { + auto f = basic_fp(); + bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) + : f.assign(converted_value); + if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; + if (fixed) dragon_flags |= dragon::fixed; + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, dragon_flags, precision, buf, exp); + } + if (!fixed && !specs.alt()) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} + +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template constexpr auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} + +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, + locale_ref loc) -> OutputIt { + // Use signbit because value < 0 is false for NaN. + sign s = detail::signbit(value) ? sign::minus : specs.sign(); + + if (!detail::isfinite(value)) + return write_nonfinite(out, detail::isnan(value), specs, s); + + if (specs.align() == align::numeric && s != sign::none) { + *out++ = detail::getsign(s); + s = sign::none; + if (specs.width != 0) --specs.width; + } + + constexpr int exp_upper = detail::exp_upper(); + int precision = specs.precision; + if (precision < 0) { + if (specs.type() != presentation_type::none) { + precision = 6; + } else if (is_fast_float::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + using floaty = conditional_t= sizeof(double), double, float>; + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, s, exp_upper, loc); + } + } + + memory_buffer buffer; + if (specs.type() == presentation_type::hexfloat) { + if (s != sign::none) buffer.push_back(detail::getsign(s)); + format_hexfloat(convert_float(value), specs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + + if (specs.type() == presentation_type::exp) { + if (precision == max_value()) + report_error("number is too big"); + else + ++precision; + if (specs.precision != 0) specs.set_alt(); + } else if (specs.type() == presentation_type::fixed) { + if (specs.precision != 0) specs.set_alt(); + } else if (precision == 0) { + precision = 1; + } + int exp = format_float(convert_float(value), precision, specs, + std::is_same(), buffer); + + specs.precision = precision; + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, s, exp_upper, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, + locale_ref loc = {}) -> OutputIt { + return specs.localized() && write_loc(out, value, specs, loc) + ? out + : write_float(out, value, specs, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) return write(out, value, format_specs()); + + auto s = detail::signbit(value) ? sign::minus : sign::none; + + constexpr auto specs = format_specs(); + using floaty = conditional_t= sizeof(double), double, float>; + using floaty_uint = typename dragonbox::float_info::carrier_uint; + floaty_uint mask = exponent_mask(); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), specs, s); + + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, s, exp_upper(), {}); +} + +template ::value && + !is_fast_float::value)> +inline auto write(OutputIt out, T value) -> OutputIt { + return write(out, value, format_specs()); +} + +template +auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) + -> OutputIt { + FMT_ASSERT(false, ""); + return out; +} + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) + -> OutputIt { + return copy_noinline(value.begin(), value.end(), out); +} + +template ::value)> +constexpr auto write(OutputIt out, const T& value) -> OutputIt { + return write(out, to_string_view(value)); +} + +// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. +template < + typename Char, typename OutputIt, typename T, + bool check = std::is_enum::value && !std::is_same::value && + mapped_type_constant::value != type::custom_type, + FMT_ENABLE_IF(check)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + return write(out, static_cast>(value)); +} + +template ::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type() != presentation_type::none && + specs.type() != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); +} + +template +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { + if (value) return write(out, basic_string_view(value)); + report_error("string pointer is null"); + return out; +} + +template ::value)> +auto write(OutputIt out, const T* value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return write_ptr(out, bit_cast(value), &specs); +} + +template ::value == + type::custom_type && + !std::is_fundamental::value)> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { + auto f = formatter(); + auto parse_ctx = parse_context({}); + f.parse(parse_ctx); + auto ctx = basic_format_context(out, {}, {}); + return f.format(value, ctx); +} + +template +using is_builtin = + bool_constant::value || FMT_BUILTIN_TYPES>; + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using context = buffered_context; + + basic_appender out; + + void operator()(monostate) { report_error("argument not found"); } + + template ::value)> + void operator()(T value) { + write(out, value); + } + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg::handle h) { + // Use a null locale since the default format must be unlocalized. + auto parse_ctx = parse_context({}); + auto format_ctx = context(out, {}, {}); + h.format(parse_ctx, format_ctx); + } +}; + +template struct arg_formatter { + basic_appender out; + const format_specs& specs; + FMT_NO_UNIQUE_ADDRESS locale_ref locale; + + template ::value)> + FMT_CONSTEXPR FMT_INLINE void operator()(T value) { + detail::write(out, value, specs, locale); + } + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg>::handle) { + // User-defined types are handled separately because they require access + // to the parse context. + } +}; + +struct dynamic_spec_getter { + template ::value)> + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { + return is_negative(value) ? ~0ull : static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { + report_error("width/precision is not integer"); + return 0; + } +}; + +template +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { + auto arg = ctx.arg(id); + if (!arg) report_error("argument not found"); + return arg; +} + +template +FMT_CONSTEXPR int get_dynamic_spec( + arg_id_kind kind, const arg_ref& ref, + Context& ctx) { + FMT_ASSERT(kind != arg_id_kind::none, ""); + auto arg = + kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); + if (!arg) report_error("argument not found"); + unsigned long long value = arg.visit(dynamic_spec_getter()); + if (value > to_unsigned(max_value())) + report_error("width/precision is out of range"); + return static_cast(value); +} + +template +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref& ref, Context& ctx) { + if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template Str> +struct static_named_arg : view { + static constexpr auto name = Str.data; + + const T& value; + static_named_arg(const T& v) : value(v) {} +}; + +template Str> +struct is_named_arg> : std::true_type {}; + +template Str> +struct is_static_named_arg> : std::true_type { +}; + +template Str> +struct udl_arg { + template auto operator=(T&& value) const { + return static_named_arg(std::forward(value)); + } +}; +#else +template struct udl_arg { + const Char* str; + + template auto operator=(T&& value) const -> named_arg { + return {str, std::forward(value)}; + } +}; +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS + +template struct format_handler { + parse_context parse_ctx; + buffered_context ctx; + + void on_text(const Char* begin, const Char* end) { + copy_noinline(begin, end, ctx.out()); + } + + FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + parse_ctx.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + parse_ctx.check_arg_id(id); + int arg_id = ctx.arg_id(id); + if (arg_id < 0) report_error("argument not found"); + return arg_id; + } + + FMT_INLINE void on_replacement_field(int id, const Char*) { + ctx.arg(id).visit(default_arg_formatter{ctx.out()}); + } + + auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + auto arg = get_arg(ctx, id); + // Not using a visitor for custom types gives better codegen. + if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); + + auto specs = dynamic_format_specs(); + begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type()); + if (specs.dynamic()) { + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + arg.visit(arg_formatter{ctx.out(), specs, ctx.locale()}); + return begin; + } + + FMT_NORETURN void on_error(const char* message) { report_error(message); } +}; + +using format_func = void (*)(detail::buffer&, int, const char*); +FMT_API void do_report_error(format_func func, int error_code, + const char* message) noexcept; + +FMT_API void format_error_code(buffer& out, int error_code, + string_view message) noexcept; + +template +template +FMT_CONSTEXPR auto native_formatter::format( + const T& val, FormatContext& ctx) const -> decltype(ctx.out()) { + if (!specs_.dynamic()) + return write(ctx.out(), val, specs_, ctx.locale()); + auto specs = format_specs(specs_); + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs_.precision_ref, ctx); + return write(ctx.out(), val, specs, ctx.locale()); +} + +// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; + +// DEPRECATED! +template struct vformat_args { + using type = basic_format_args>; +}; +template <> struct vformat_args { + using type = format_args; +}; + +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}) { + auto out = basic_appender(buf); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} +} // namespace detail + +FMT_BEGIN_EXPORT + +// A generic formatting context with custom output iterator and character +// (code unit) support. Char is the format string code unit type which can be +// different from OutputIt::value_type. +template class generic_context { + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using char_type = Char; + using iterator = OutputIt; + using parse_context_type FMT_DEPRECATED = parse_context; + template + using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + constexpr generic_context(OutputIt out, + basic_format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + generic_context(generic_context&&) = default; + generic_context(const generic_context&) = delete; + void operator=(const generic_context&) = delete; + + constexpr auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } + auto arg(basic_string_view name) const + -> basic_format_arg { + return args_.get(name); + } + constexpr auto arg_id(basic_string_view name) const -> int { + return args_.get_id(name); + } + + constexpr auto out() const -> iterator { return out_; } + + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + constexpr auto locale() const -> detail::locale_ref { return loc_; } +}; + +class loc_value { + private: + basic_format_arg value_; + + public: + template ::value)> + loc_value(T value) : value_(value) {} + + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return value_.visit(vis); + } +}; + +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; + + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs& specs) const -> bool; + + public: + static FMT_API typename Locale::id id; + + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", std::string grouping = "\3", + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(grouping), + decimal_point_(decimal_point) {} + + auto put(appender out, loc_value val, const format_specs& specs) const + -> bool { + return do_put(out, val, specs); + } +}; + +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(value, ctx); \ + } \ + } + +FMT_FORMAT_AS(signed char, int); +FMT_FORMAT_AS(unsigned char, unsigned); +FMT_FORMAT_AS(short, int); +FMT_FORMAT_AS(unsigned short, unsigned); +FMT_FORMAT_AS(long, detail::long_type); +FMT_FORMAT_AS(unsigned long, detail::ulong_type); +FMT_FORMAT_AS(Char*, const Char*); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(void*, const void*); + +template +struct formatter : formatter, Char> {}; + +template +class formatter, Char> + : public formatter, Char> {}; + +template +struct formatter, Char> : formatter {}; +template +struct formatter, Char> + : formatter {}; + +template +struct formatter + : detail::native_formatter {}; + +template +struct formatter>> + : formatter, Char> { + template + FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto&& val = format_as(value); // Make an lvalue reference for format. + return formatter, Char>::format(val, ctx); + } +}; + +/** + * Converts `p` to `const void*` for pointer formatting. + * + * **Example**: + * + * auto s = fmt::format("{}", fmt::ptr(p)); + */ +template auto ptr(T p) -> const void* { + static_assert(std::is_pointer::value, ""); + return detail::bit_cast(p); +} + +/** + * Converts `e` to the underlying type. + * + * **Example**: + * + * enum class color { red, green, blue }; + * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0" + */ +template +constexpr auto underlying(Enum e) noexcept -> underlying_t { + return static_cast>(e); +} + +namespace enums { +template ::value)> +constexpr auto format_as(Enum e) noexcept -> underlying_t { + return static_cast>(e); +} +} // namespace enums + +#ifdef __cpp_lib_byte +template <> struct formatter : formatter { + static auto format_as(std::byte b) -> unsigned char { + return static_cast(b); + } + template + auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { + return formatter::format(format_as(b), ctx); + } +}; +#endif + +struct bytes { + string_view data; + + inline explicit bytes(string_view s) : data(s) {} +}; + +template <> struct formatter { + private: + detail::dynamic_format_specs<> specs_; + + public: + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type::string_type); + } + + template + auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + return detail::write_bytes(ctx.out(), b.data, specs); + } +}; + +// group_digits_view is not derived from view because it copies the argument. +template struct group_digits_view { + T value; +}; + +/** + * Returns a view that formats an integer value using ',' as a + * locale-independent thousands separator. + * + * **Example**: + * + * fmt::print("{}", fmt::group_digits(12345)); + * // Output: "12,345" + */ +template auto group_digits(T value) -> group_digits_view { + return {value}; +} + +template struct formatter> : formatter { + private: + detail::dynamic_format_specs<> specs_; + + public: + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type::int_type); + } + + template + auto format(group_digits_view view, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + auto arg = detail::make_write_int_arg(view.value, specs.sign()); + return detail::write_int( + ctx.out(), static_cast>(arg.abs_value), + arg.prefix, specs, detail::digit_grouping("\3", ",")); + } +}; + +template struct nested_view { + const formatter* fmt; + const T* value; +}; + +template +struct formatter, Char> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + template + auto format(nested_view view, FormatContext& ctx) const + -> decltype(ctx.out()) { + return view.fmt->format(*view.value, ctx); + } +}; + +template struct nested_formatter { + private: + basic_specs specs_; + int width_; + formatter formatter_; + + public: + constexpr nested_formatter() : width_(0) {} + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + auto specs = format_specs(); + it = detail::parse_align(it, end, specs); + specs_ = specs; + Char c = *it; + auto width_ref = detail::arg_ref(); + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs, width_ref, ctx); + width_ = specs.width; + } + ctx.advance_to(it); + return formatter_.parse(ctx); + } + + template + auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) { + if (width_ == 0) return write(ctx.out()); + auto buf = basic_memory_buffer(); + write(basic_appender(buf)); + auto specs = format_specs(); + specs.width = width_; + specs.copy_fill_from(specs_); + specs.set_align(specs_.align()); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } + + auto nested(const T& value) const -> nested_view { + return nested_view{&formatter_, &value}; + } +}; + +inline namespace literals { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template constexpr auto operator""_a() { + using char_t = remove_cvref_t; + return detail::udl_arg(); +} +#else +/** + * User-defined literal equivalent of `fmt::arg`. + * + * **Example**: + * + * using namespace fmt::literals; + * fmt::print("The answer is {answer}.", "answer"_a=42); + */ +constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { + return {s}; +} +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS +} // namespace literals + +/// A fast integer formatter. +class format_int { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum { buffer_size = std::numeric_limits::digits10 + 3 }; + mutable char buffer_[buffer_size]; + char* str_; + + template + FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* { + auto n = static_cast>(value); + return detail::do_format_decimal(buffer_, n, buffer_size - 1); + } + + template + FMT_CONSTEXPR20 auto format_signed(Int value) -> char* { + auto abs_value = static_cast>(value); + bool negative = value < 0; + if (negative) abs_value = 0 - abs_value; + auto begin = format_unsigned(abs_value); + if (negative) *--begin = '-'; + return begin; + } + + public: + FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long long value) + : str_(format_unsigned(value)) {} + + /// Returns the number of characters written to the output buffer. + FMT_CONSTEXPR20 auto size() const -> size_t { + return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + } + + /// Returns a pointer to the output buffer content. No terminating null + /// character is appended. + FMT_CONSTEXPR20 auto data() const -> const char* { return str_; } + + /// Returns a pointer to the output buffer content with terminating null + /// character appended. + FMT_CONSTEXPR20 auto c_str() const -> const char* { + buffer_[buffer_size - 1] = '\0'; + return str_; + } + + /// Returns the content of the output buffer as an `std::string`. + inline auto str() const -> std::string { return {str_, size()}; } +}; + +#define FMT_STRING_IMPL(s, base) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + constexpr explicit operator fmt::basic_string_view() const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() + +/** + * Constructs a legacy compile-time format string from a string literal `s`. + * + * **Example**: + * + * // A compile-time error because 'd' is an invalid specifier for strings. + * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) + +FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error; + +/** + * Constructs `std::system_error` with a message formatted with + * `fmt::format(fmt, args...)`. + * `error_code` is a system error code as given by `errno`. + * + * **Example**: + * + * // This throws std::system_error with the description + * // cannot open file 'madeup': No such file or directory + * // or similar (system message may vary). + * const char* filename = "madeup"; + * FILE* file = fopen(filename, "r"); + * if (!file) + * throw fmt::system_error(errno, "cannot open file '{}'", filename); + */ +template +auto system_error(int error_code, format_string fmt, T&&... args) + -> std::system_error { + return vsystem_error(error_code, fmt.str, vargs{{args...}}); +} + +/** + * Formats an error message for an error returned by an operating system or a + * language runtime, for example a file opening error, and writes it to `out`. + * The format is the same as the one used by `std::system_error(ec, message)` + * where `ec` is `std::error_code(error_code, std::generic_category())`. + * It is implementation-defined but normally looks like: + * + * : + * + * where `` is the passed message and `` is the system + * message corresponding to the error code. + * `error_code` is a system error code as given by `errno`. + */ +FMT_API void format_system_error(detail::buffer& out, int error_code, + const char* message) noexcept; + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_system_error(int error_code, const char* message) noexcept; + +template ::value)> +inline auto vformat(const Locale& loc, string_view fmt, format_args args) + -> std::string { + auto buf = memory_buffer(); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return {buf.data(), buf.size()}; +} + +template ::value)> +FMT_INLINE auto format(const Locale& loc, format_string fmt, T&&... args) + -> std::string { + return vformat(loc, fmt.str, vargs{{args...}}); +} + +template ::value)> +auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, + format_args args) -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return detail::get_iterator(buf, out); +} + +template ::value&& + detail::is_locale::value)> +FMT_INLINE auto format_to(OutputIt out, const Locale& loc, + format_string fmt, T&&... args) -> OutputIt { + return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); +} + +template ::value)> +FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc, + format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, + detail::locale_ref(loc)); + return buf.count(); +} + +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + * Formats `args` according to specifications in `fmt` and returns the result + * as a string. + * + * **Example**: + * + * #include + * std::string message = fmt::format("The answer is {}.", 42); + */ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt.str, vargs{{args...}}); +} + +/** + * Converts `value` to `std::string` using the default format for type `T`. + * + * **Example**: + * + * std::string answer = fmt::to_string(42); + */ +template ::value)> +FMT_NODISCARD auto to_string(T value) -> std::string { + // The buffer should be large enough to store the number including the sign + // or "false" for bool. + char buffer[max_of(detail::digits10() + 2, 5)]; + return {buffer, detail::write(buffer, value)}; +} + +template ::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + return to_string(format_as(value)); +} + +template ::value && + !detail::use_format_as::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + auto buffer = memory_buffer(); + detail::write(appender(buffer), value); + return {buffer.data(), buffer.size()}; +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline +# include "format-inl.h" +#endif + +// Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES. +#ifdef FMT_REMOVE_TRANSITIVE_INCLUDES +# undef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +#endif + +#endif // FMT_FORMAT_H_ diff --git a/deps/fmt/include/fmt/os.h b/deps/fmt/include/fmt/os.h new file mode 100644 index 00000000000..b2cc5e4b85f --- /dev/null +++ b/deps/fmt/include/fmt/os.h @@ -0,0 +1,427 @@ +// Formatting library for C++ - optional OS-specific functionality +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_OS_H_ +#define FMT_OS_H_ + +#include "format.h" + +#ifndef FMT_MODULE +# include +# include +# include +# include // std::system_error + +# if FMT_HAS_INCLUDE() +# include // LC_NUMERIC_MASK on macOS +# endif +#endif // FMT_MODULE + +#ifndef FMT_USE_FCNTL +// UWP doesn't provide _pipe. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include // for O_RDONLY +# define FMT_USE_FCNTL 1 +# else +# define FMT_USE_FCNTL 0 +# endif +#endif + +#ifndef FMT_POSIX +# if defined(_WIN32) && !defined(__MINGW32__) +// Fix warnings about deprecated symbols. +# define FMT_POSIX(call) _##call +# else +# define FMT_POSIX(call) call +# endif +#endif + +// Calls to system functions are wrapped in FMT_SYSTEM for testability. +#ifdef FMT_SYSTEM +# define FMT_HAS_SYSTEM +# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) +#else +# define FMT_SYSTEM(call) ::call +# ifdef _WIN32 +// Fix warnings about deprecated symbols. +# define FMT_POSIX_CALL(call) ::_##call +# else +# define FMT_POSIX_CALL(call) ::call +# endif +#endif + +// Retries the expression while it evaluates to error_result and errno +// equals to EINTR. +#ifndef _WIN32 +# define FMT_RETRY_VAL(result, expression, error_result) \ + do { \ + (result) = (expression); \ + } while ((result) == (error_result) && errno == EINTR) +#else +# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) +#endif + +#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) + +FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT + +/** + * A reference to a null-terminated string. It can be constructed from a C + * string or `std::string`. + * + * You can use one of the following type aliases for common character types: + * + * +---------------+-----------------------------+ + * | Type | Definition | + * +===============+=============================+ + * | cstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * | wcstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * + * This class is most useful as a parameter type for functions that wrap C APIs. + */ +template class basic_cstring_view { + private: + const Char* data_; + + public: + /// Constructs a string reference object from a C string. + basic_cstring_view(const Char* s) : data_(s) {} + + /// Constructs a string reference from an `std::string` object. + basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} + + /// Returns the pointer to a C string. + auto c_str() const -> const Char* { return data_; } +}; + +using cstring_view = basic_cstring_view; +using wcstring_view = basic_cstring_view; + +#ifdef _WIN32 +FMT_API const std::error_category& system_category() noexcept; + +namespace detail { +FMT_API void format_windows_error(buffer& out, int error_code, + const char* message) noexcept; +} + +FMT_API std::system_error vwindows_error(int error_code, string_view fmt, + format_args args); + +/** + * Constructs a `std::system_error` object with the description of the form + * + * : + * + * where `` is the formatted message and `` is the + * system message corresponding to the error code. + * `error_code` is a Windows error code as given by `GetLastError`. + * If `error_code` is not a valid error code such as -1, the system message + * will look like "error -1". + * + * **Example**: + * + * // This throws a system_error with the description + * // cannot open file 'madeup': The system cannot find the file + * specified. + * // or similar (system message may vary). + * const char *filename = "madeup"; + * LPOFSTRUCT of = LPOFSTRUCT(); + * HFILE file = OpenFile(filename, &of, OF_READ); + * if (file == HFILE_ERROR) { + * throw fmt::windows_error(GetLastError(), + * "cannot open file '{}'", filename); + * } + */ +template +auto windows_error(int error_code, string_view message, const T&... args) + -> std::system_error { + return vwindows_error(error_code, message, vargs{{args...}}); +} + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_windows_error(int error_code, const char* message) noexcept; +#else +inline auto system_category() noexcept -> const std::error_category& { + return std::system_category(); +} +#endif // _WIN32 + +// std::system is not available on some platforms such as iOS (#2248). +#ifdef __OSX__ +template > +void say(const S& fmt, Args&&... args) { + std::system(format("say \"{}\"", format(fmt, args...)).c_str()); +} +#endif + +// A buffered file. +class buffered_file { + private: + FILE* file_; + + friend class file; + + inline explicit buffered_file(FILE* f) : file_(f) {} + + public: + buffered_file(const buffered_file&) = delete; + void operator=(const buffered_file&) = delete; + + // Constructs a buffered_file object which doesn't represent any file. + inline buffered_file() noexcept : file_(nullptr) {} + + // Destroys the object closing the file it represents if any. + FMT_API ~buffered_file() noexcept; + + public: + inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { + other.file_ = nullptr; + } + + inline auto operator=(buffered_file&& other) -> buffered_file& { + close(); + file_ = other.file_; + other.file_ = nullptr; + return *this; + } + + // Opens a file. + FMT_API buffered_file(cstring_view filename, cstring_view mode); + + // Closes the file. + FMT_API void close(); + + // Returns the pointer to a FILE object representing this file. + inline auto get() const noexcept -> FILE* { return file_; } + + FMT_API auto descriptor() const -> int; + + template + inline void print(string_view fmt, const T&... args) { + fmt::vargs vargs = {{args...}}; + detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) + : fmt::vprint(file_, fmt, vargs); + } +}; + +#if FMT_USE_FCNTL + +// A file. Closed file is represented by a file object with descriptor -1. +// Methods that are not declared with noexcept may throw +// fmt::system_error in case of failure. Note that some errors such as +// closing the file multiple times will cause a crash on Windows rather +// than an exception. You can get standard behavior by overriding the +// invalid parameter handler with _set_invalid_parameter_handler. +class FMT_API file { + private: + int fd_; // File descriptor. + + // Constructs a file object with a given descriptor. + explicit file(int fd) : fd_(fd) {} + + friend struct pipe; + + public: + // Possible values for the oflag argument to the constructor. + enum { + RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. + WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. + RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. + CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. + APPEND = FMT_POSIX(O_APPEND), // Open in append mode. + TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. + }; + + // Constructs a file object which doesn't represent any file. + inline file() noexcept : fd_(-1) {} + + // Opens a file and constructs a file object representing this file. + file(cstring_view path, int oflag); + + public: + file(const file&) = delete; + void operator=(const file&) = delete; + + inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } + + // Move assignment is not noexcept because close may throw. + inline auto operator=(file&& other) -> file& { + close(); + fd_ = other.fd_; + other.fd_ = -1; + return *this; + } + + // Destroys the object closing the file it represents if any. + ~file() noexcept; + + // Returns the file descriptor. + inline auto descriptor() const noexcept -> int { return fd_; } + + // Closes the file. + void close(); + + // Returns the file size. The size has signed type for consistency with + // stat::st_size. + auto size() const -> long long; + + // Attempts to read count bytes from the file into the specified buffer. + auto read(void* buffer, size_t count) -> size_t; + + // Attempts to write count bytes from the specified buffer to the file. + auto write(const void* buffer, size_t count) -> size_t; + + // Duplicates a file descriptor with the dup function and returns + // the duplicate as a file object. + static auto dup(int fd) -> file; + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd); + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd, std::error_code& ec) noexcept; + + // Creates a buffered_file object associated with this file and detaches + // this file object from the file. + auto fdopen(const char* mode) -> buffered_file; + +# if defined(_WIN32) && !defined(__MINGW32__) + // Opens a file and constructs a file object representing this file by + // wcstring_view filename. Windows only. + static file open_windows_file(wcstring_view path, int oflag); +# endif +}; + +struct FMT_API pipe { + file read_end; + file write_end; + + // Creates a pipe setting up read_end and write_end file objects for reading + // and writing respectively. + pipe(); +}; + +// Returns the memory page size. +auto getpagesize() -> long; + +namespace detail { + +struct buffer_size { + constexpr buffer_size() = default; + size_t value = 0; + FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { + auto bs = buffer_size(); + bs.value = val; + return bs; + } +}; + +struct ostream_params { + int oflag = file::WRONLY | file::CREATE | file::TRUNC; + size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; + + constexpr ostream_params() {} + + template + ostream_params(T... params, int new_oflag) : ostream_params(params...) { + oflag = new_oflag; + } + + template + ostream_params(T... params, detail::buffer_size bs) + : ostream_params(params...) { + this->buffer_size = bs.value; + } + +// Intel has a bug that results in failure to deduce a constructor +// for empty parameter packs. +# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 + ostream_params(int new_oflag) : oflag(new_oflag) {} + ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} +# endif +}; + +} // namespace detail + +FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); + +/// A fast buffered output stream for writing from a single thread. Writing from +/// multiple threads without external synchronization may result in a data race. +class FMT_API ostream : private detail::buffer { + private: + file file_; + + ostream(cstring_view path, const detail::ostream_params& params); + + static void grow(buffer& buf, size_t); + + public: + ostream(ostream&& other) noexcept; + ~ostream(); + + operator writer() { + detail::buffer& buf = *this; + return buf; + } + + inline void flush() { + if (size() == 0) return; + file_.write(data(), size() * sizeof(data()[0])); + clear(); + } + + template + friend auto output_file(cstring_view path, T... params) -> ostream; + + inline void close() { + flush(); + file_.close(); + } + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + vformat_to(appender(*this), fmt.str, vargs{{args...}}); + } +}; + +/** + * Opens a file for writing. Supported parameters passed in `params`: + * + * - ``: Flags passed to [open]( + * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) + * (`file::WRONLY | file::CREATE | file::TRUNC` by default) + * - `buffer_size=`: Output buffer size + * + * **Example**: + * + * auto out = fmt::output_file("guide.txt"); + * out.print("Don't {}", "Panic"); + */ +template +inline auto output_file(cstring_view path, T... params) -> ostream { + return {path, detail::ostream_params(params...)}; +} +#endif // FMT_USE_FCNTL + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_OS_H_ diff --git a/deps/fmt/include/fmt/ostream.h b/deps/fmt/include/fmt/ostream.h new file mode 100644 index 00000000000..7bec4efe2b3 --- /dev/null +++ b/deps/fmt/include/fmt/ostream.h @@ -0,0 +1,167 @@ +// Formatting library for C++ - std::ostream support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_OSTREAM_H_ +#define FMT_OSTREAM_H_ + +#ifndef FMT_MODULE +# include // std::filebuf +#endif + +#ifdef _WIN32 +# ifdef __GLIBCXX__ +# include +# include +# endif +# include +#endif + +#include "chrono.h" // formatbuf + +#ifdef _MSVC_STL_UPDATE +# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE +#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5 +# define FMT_MSVC_STL_UPDATE _MSVC_LANG +#else +# define FMT_MSVC_STL_UPDATE 0 +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +// Generate a unique explicit instantion in every translation unit using a tag +// type in an anonymous namespace. +namespace { +struct file_access_tag {}; +} // namespace +template +class file_access { + friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } +}; + +#if FMT_MSVC_STL_UPDATE +template class file_access; +auto get_file(std::filebuf&) -> FILE*; +#endif + +// Write the content of buf to os. +// It is a separate function rather than a part of vprint to simplify testing. +template +void write_buffer(std::basic_ostream& os, buffer& buf) { + const Char* buf_data = buf.data(); + using unsigned_streamsize = make_unsigned_t; + unsigned_streamsize size = buf.size(); + unsigned_streamsize max_size = to_unsigned(max_value()); + do { + unsigned_streamsize n = size <= max_size ? size : max_size; + os.write(buf_data, static_cast(n)); + buf_data += n; + size -= n; + } while (size != 0); +} + +template struct streamed_view { + const T& value; +}; +} // namespace detail + +// Formats an object of type T that has an overloaded ostream operator<<. +template +struct basic_ostream_formatter : formatter, Char> { + void set_debug_format() = delete; + + template + auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { + auto buffer = basic_memory_buffer(); + auto&& formatbuf = detail::formatbuf>(buffer); + auto&& output = std::basic_ostream(&formatbuf); + output.imbue(std::locale::classic()); // The default is always unlocalized. + output << value; + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); + return formatter, Char>::format( + {buffer.data(), buffer.size()}, ctx); + } +}; + +using ostream_formatter = basic_ostream_formatter; + +template +struct formatter, Char> + : basic_ostream_formatter { + template + auto format(detail::streamed_view view, Context& ctx) const + -> decltype(ctx.out()) { + return basic_ostream_formatter::format(view.value, ctx); + } +}; + +/** + * Returns a view that formats `value` via an ostream `operator<<`. + * + * **Example**: + * + * fmt::print("Current thread id: {}\n", + * fmt::streamed(std::this_thread::get_id())); + */ +template +constexpr auto streamed(const T& value) -> detail::streamed_view { + return {value}; +} + +inline void vprint(std::ostream& os, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + FILE* f = nullptr; +#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI + if (auto* buf = dynamic_cast(os.rdbuf())) + f = detail::get_file(*buf); +#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI + auto* rdbuf = os.rdbuf(); + if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) + f = sfbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) + f = fbuf->file(); +#endif +#ifdef _WIN32 + if (f) { + int fd = _fileno(f); + if (_isatty(fd)) { + os.flush(); + if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; + } + } +#endif + detail::ignore_unused(f); + detail::write_buffer(os, buffer); +} + +/** + * Prints formatted data to the stream `os`. + * + * **Example**: + * + * fmt::print(cerr, "Don't {}!", "panic"); + */ +FMT_EXPORT template +void print(std::ostream& os, format_string fmt, T&&... args) { + fmt::vargs vargs = {{args...}}; + if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs); + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt.str, vargs); + detail::write_buffer(os, buffer); +} + +FMT_EXPORT template +void println(std::ostream& os, format_string fmt, T&&... args) { + fmt::print(os, FMT_STRING("{}\n"), + fmt::format(fmt, std::forward(args)...)); +} + +FMT_END_NAMESPACE + +#endif // FMT_OSTREAM_H_ diff --git a/deps/fmt/include/fmt/printf.h b/deps/fmt/include/fmt/printf.h new file mode 100644 index 00000000000..e7268401854 --- /dev/null +++ b/deps/fmt/include/fmt/printf.h @@ -0,0 +1,633 @@ +// Formatting library for C++ - legacy printf implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_PRINTF_H_ +#define FMT_PRINTF_H_ + +#ifndef FMT_MODULE +# include // std::max +# include // std::numeric_limits +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT + +template struct printf_formatter { + printf_formatter() = delete; +}; + +template class basic_printf_context { + private: + basic_appender out_; + basic_format_args args_; + + static_assert(std::is_same::value || + std::is_same::value, + "Unsupported code unit type."); + + public: + using char_type = Char; + using parse_context_type = parse_context; + template using formatter_type = printf_formatter; + enum { builtin_types = 1 }; + + /// Constructs a `printf_context` object. References to the arguments are + /// stored in the context object so make sure they have appropriate lifetimes. + basic_printf_context(basic_appender out, + basic_format_args args) + : out_(out), args_(args) {} + + auto out() -> basic_appender { return out_; } + void advance_to(basic_appender) {} + + auto locale() -> detail::locale_ref { return {}; } + + auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } +}; + +namespace detail { + +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = + static_cast(memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template struct int_checker { + template static auto fits_in_int(T value) -> bool { + unsigned max = to_unsigned(max_value()); + return value <= max; + } + inline static auto fits_in_int(bool) -> bool { return true; } +}; + +template <> struct int_checker { + template static auto fits_in_int(T value) -> bool { + return value >= (std::numeric_limits::min)() && + value <= max_value(); + } + inline static auto fits_in_int(int) -> bool { return true; } +}; + +struct printf_precision_handler { + template ::value)> + auto operator()(T value) -> int { + if (!int_checker::is_signed>::fits_in_int(value)) + report_error("number is too big"); + return (std::max)(static_cast(value), 0); + } + + template ::value)> + auto operator()(T) -> int { + report_error("precision is not integer"); + return 0; + } +}; + +// An argument visitor that returns true iff arg is a zero integer. +struct is_zero_int { + template ::value)> + auto operator()(T value) -> bool { + return value == 0; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + +template struct make_unsigned_or_bool : std::make_unsigned {}; + +template <> struct make_unsigned_or_bool { + using type = bool; +}; + +template class arg_converter { + private: + using char_type = typename Context::char_type; + + basic_format_arg& arg_; + char_type type_; + + public: + arg_converter(basic_format_arg& arg, char_type type) + : arg_(arg), type_(type) {} + + void operator()(bool value) { + if (type_ != 's') operator()(value); + } + + template ::value)> + void operator()(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using target_type = conditional_t::value, U, T>; + if (const_check(sizeof(target_type) <= sizeof(int))) { + // Extra casts are used to silence warnings. + using unsigned_type = typename make_unsigned_or_bool::type; + if (is_signed) + arg_ = static_cast(static_cast(value)); + else + arg_ = static_cast(static_cast(value)); + } else { + // glibc's printf doesn't sign extend arguments of smaller types: + // std::printf("%lld", -42); // prints "4294967254" + // but we don't have to do the same because it's a UB. + if (is_signed) + arg_ = static_cast(value); + else + arg_ = static_cast::type>(value); + } + } + + template ::value)> + void operator()(U) {} // No conversion needed for non-integral types. +}; + +// Converts an integer argument to T for printf, if T is an integral type. +// If T is void, the argument is converted to corresponding signed or unsigned +// type depending on the type specifier: 'd' and 'i' - signed, other - +// unsigned). +template +void convert_arg(basic_format_arg& arg, Char type) { + arg.visit(arg_converter(arg, type)); +} + +// Converts an integer argument to char for printf. +template class char_converter { + private: + basic_format_arg& arg_; + + public: + explicit char_converter(basic_format_arg& arg) : arg_(arg) {} + + template ::value)> + void operator()(T value) { + arg_ = static_cast(value); + } + + template ::value)> + void operator()(T) {} // No conversion needed for non-integral types. +}; + +// An argument visitor that return a pointer to a C string if argument is a +// string or null otherwise. +template struct get_cstring { + template auto operator()(T) -> const Char* { return nullptr; } + auto operator()(const Char* s) -> const Char* { return s; } +}; + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class printf_width_handler { + private: + format_specs& specs_; + + public: + inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {} + + template ::value)> + auto operator()(T value) -> unsigned { + auto width = static_cast>(value); + if (detail::is_negative(value)) { + specs_.set_align(align::left); + width = 0 - width; + } + unsigned int_max = to_unsigned(max_value()); + if (width > int_max) report_error("number is too big"); + return static_cast(width); + } + + template ::value)> + auto operator()(T) -> unsigned { + report_error("width is not integer"); + return 0; + } +}; + +// Workaround for a bug with the XL compiler when initializing +// printf_arg_formatter's base class. +template +auto make_arg_formatter(basic_appender iter, format_specs& s) + -> arg_formatter { + return {iter, s, locale_ref()}; +} + +// The `printf` argument formatter. +template +class printf_arg_formatter : public arg_formatter { + private: + using base = arg_formatter; + using context_type = basic_printf_context; + + context_type& context_; + + void write_null_pointer(bool is_string = false) { + auto s = this->specs; + s.set_type(presentation_type::none); + write_bytes(this->out, is_string ? "(null)" : "(nil)", s); + } + + template void write(T value) { + detail::write(this->out, value, this->specs, this->locale); + } + + public: + printf_arg_formatter(basic_appender iter, format_specs& s, + context_type& ctx) + : base(make_arg_formatter(iter, s)), context_(ctx) {} + + void operator()(monostate value) { write(value); } + + template ::value)> + void operator()(T value) { + // MSVC2013 fails to compile separate overloads for bool and Char so use + // std::is_same instead. + if (!std::is_same::value) { + write(value); + return; + } + format_specs s = this->specs; + if (s.type() != presentation_type::none && + s.type() != presentation_type::chr) { + return (*this)(static_cast(value)); + } + s.set_sign(sign::none); + s.clear_alt(); + s.set_fill(' '); // Ignore '0' flag for char types. + // align::numeric needs to be overwritten here since the '0' flag is + // ignored for non-numeric types + if (s.align() == align::none || s.align() == align::numeric) + s.set_align(align::right); + detail::write(this->out, static_cast(value), s); + } + + template ::value)> + void operator()(T value) { + write(value); + } + + void operator()(const char* value) { + if (value) + write(value); + else + write_null_pointer(this->specs.type() != presentation_type::pointer); + } + + void operator()(const wchar_t* value) { + if (value) + write(value); + else + write_null_pointer(this->specs.type() != presentation_type::pointer); + } + + void operator()(basic_string_view value) { write(value); } + + void operator()(const void* value) { + if (value) + write(value); + else + write_null_pointer(); + } + + void operator()(typename basic_format_arg::handle handle) { + auto parse_ctx = parse_context({}); + handle.format(parse_ctx, context_); + } +}; + +template +void parse_flags(format_specs& specs, const Char*& it, const Char* end) { + for (; it != end; ++it) { + switch (*it) { + case '-': specs.set_align(align::left); break; + case '+': specs.set_sign(sign::plus); break; + case '0': specs.set_fill('0'); break; + case ' ': + if (specs.sign() != sign::plus) specs.set_sign(sign::space); + break; + case '#': specs.set_alt(); break; + default: return; + } + } +} + +template +auto parse_header(const Char*& it, const Char* end, format_specs& specs, + GetArg get_arg) -> int { + int arg_index = -1; + Char c = *it; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + int value = parse_nonnegative_int(it, end, -1); + if (it != end && *it == '$') { // value is an argument index + ++it; + arg_index = value != -1 ? value : max_value(); + } else { + if (c == '0') specs.set_fill('0'); + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + if (value == -1) report_error("number is too big"); + specs.width = value; + return arg_index; + } + } + } + parse_flags(specs, it, end); + // Parse width. + if (it != end) { + if (*it >= '0' && *it <= '9') { + specs.width = parse_nonnegative_int(it, end, -1); + if (specs.width == -1) report_error("number is too big"); + } else if (*it == '*') { + ++it; + specs.width = static_cast( + get_arg(-1).visit(detail::printf_width_handler(specs))); + } + } + return arg_index; +} + +inline auto parse_printf_presentation_type(char c, type t, bool& upper) + -> presentation_type { + using pt = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + switch (c) { + case 'd': return in(t, integral_set) ? pt::dec : pt::none; + case 'o': return in(t, integral_set) ? pt::oct : pt::none; + case 'X': upper = true; FMT_FALLTHROUGH; + case 'x': return in(t, integral_set) ? pt::hex : pt::none; + case 'E': upper = true; FMT_FALLTHROUGH; + case 'e': return in(t, float_set) ? pt::exp : pt::none; + case 'F': upper = true; FMT_FALLTHROUGH; + case 'f': return in(t, float_set) ? pt::fixed : pt::none; + case 'G': upper = true; FMT_FALLTHROUGH; + case 'g': return in(t, float_set) ? pt::general : pt::none; + case 'A': upper = true; FMT_FALLTHROUGH; + case 'a': return in(t, float_set) ? pt::hexfloat : pt::none; + case 'c': return in(t, integral_set) ? pt::chr : pt::none; + case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none; + case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; + default: return pt::none; + } +} + +template +void vprintf(buffer& buf, basic_string_view format, + basic_format_args args) { + using iterator = basic_appender; + auto out = iterator(buf); + auto context = basic_printf_context(out, args); + auto parse_ctx = parse_context(format); + + // Returns the argument with specified index or, if arg_index is -1, the next + // argument. + auto get_arg = [&](int arg_index) { + if (arg_index < 0) + arg_index = parse_ctx.next_arg_id(); + else + parse_ctx.check_arg_id(--arg_index); + return detail::get_arg(context, arg_index); + }; + + const Char* start = parse_ctx.begin(); + const Char* end = parse_ctx.end(); + auto it = start; + while (it != end) { + if (!find(it, end, '%', it)) { + it = end; // find leaves it == nullptr if it doesn't find '%'. + break; + } + Char c = *it++; + if (it != end && *it == c) { + write(out, basic_string_view(start, to_unsigned(it - start))); + start = ++it; + continue; + } + write(out, basic_string_view(start, to_unsigned(it - 1 - start))); + + auto specs = format_specs(); + specs.set_align(align::right); + + // Parse argument index, flags and width. + int arg_index = parse_header(it, end, specs, get_arg); + if (arg_index == 0) report_error("argument not found"); + + // Parse precision. + if (it != end && *it == '.') { + ++it; + c = it != end ? *it : 0; + if ('0' <= c && c <= '9') { + specs.precision = parse_nonnegative_int(it, end, 0); + } else if (c == '*') { + ++it; + specs.precision = + static_cast(get_arg(-1).visit(printf_precision_handler())); + } else { + specs.precision = 0; + } + } + + auto arg = get_arg(arg_index); + // For d, i, o, u, x, and X conversion specifiers, if a precision is + // specified, the '0' flag is ignored + if (specs.precision >= 0 && is_integral_type(arg.type())) { + // Ignore '0' for non-numeric types or if '-' present. + specs.set_fill(' '); + } + if (specs.precision >= 0 && arg.type() == type::cstring_type) { + auto str = arg.visit(get_cstring()); + auto str_end = str + specs.precision; + auto nul = std::find(str, str_end, Char()); + auto sv = basic_string_view( + str, to_unsigned(nul != str_end ? nul - str : specs.precision)); + arg = sv; + } + if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt(); + if (specs.fill_unit() == '0') { + if (is_arithmetic_type(arg.type()) && specs.align() != align::left) { + specs.set_align(align::numeric); + } else { + // Ignore '0' flag for non-numeric types or if '-' flag is also present. + specs.set_fill(' '); + } + } + + // Parse length and convert the argument to the required type. + c = it != end ? *it++ : 0; + Char t = it != end ? *it : 0; + switch (c) { + case 'h': + if (t == 'h') { + ++it; + t = it != end ? *it : 0; + convert_arg(arg, t); + } else { + convert_arg(arg, t); + } + break; + case 'l': + if (t == 'l') { + ++it; + t = it != end ? *it : 0; + convert_arg(arg, t); + } else { + convert_arg(arg, t); + } + break; + case 'j': convert_arg(arg, t); break; + case 'z': convert_arg(arg, t); break; + case 't': convert_arg(arg, t); break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: --it; convert_arg(arg, c); + } + + // Parse type. + if (it == end) report_error("invalid format string"); + char type = static_cast(*it++); + if (is_integral_type(arg.type())) { + // Normalize type. + switch (type) { + case 'i': + case 'u': type = 'd'; break; + case 'c': + arg.visit(char_converter>(arg)); + break; + } + } + bool upper = false; + specs.set_type(parse_printf_presentation_type(type, arg.type(), upper)); + if (specs.type() == presentation_type::none) + report_error("invalid format specifier"); + if (upper) specs.set_upper(); + + start = it; + + // Format argument. + arg.visit(printf_arg_formatter(out, specs, context)); + } + write(out, basic_string_view(start, to_unsigned(it - start))); +} +} // namespace detail + +using printf_context = basic_printf_context; +using wprintf_context = basic_printf_context; + +using printf_args = basic_format_args; +using wprintf_args = basic_format_args; + +/// Constructs an `format_arg_store` object that contains references to +/// arguments and can be implicitly converted to `printf_args`. +template +inline auto make_printf_args(T&... args) + -> decltype(fmt::make_format_args>(args...)) { + return fmt::make_format_args>(args...); +} + +template struct vprintf_args { + using type = basic_format_args>; +}; + +template +inline auto vsprintf(basic_string_view fmt, + typename vprintf_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vprintf(buf, fmt, args); + return {buf.data(), buf.size()}; +} + +/** + * Formats `args` according to specifications in `fmt` and returns the result + * as as string. + * + * **Example**: + * + * std::string message = fmt::sprintf("The answer is %d", 42); + */ +template > +inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { + return vsprintf(detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template +inline auto vfprintf(std::FILE* f, basic_string_view fmt, + typename vprintf_args::type args) -> int { + auto buf = basic_memory_buffer(); + detail::vprintf(buf, fmt, args); + size_t size = buf.size(); + return std::fwrite(buf.data(), sizeof(Char), size, f) < size + ? -1 + : static_cast(size); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `f`. + * + * **Example**: + * + * fmt::fprintf(stderr, "Don't %s!", "panic"); + */ +template > +inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { + return vfprintf(f, detail::to_string_view(fmt), + make_printf_args(args...)); +} + +template +FMT_DEPRECATED inline auto vprintf(basic_string_view fmt, + typename vprintf_args::type args) + -> int { + return vfprintf(stdout, fmt, args); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::printf("Elapsed time: %.2f seconds", 1.23); + */ +template +inline auto printf(string_view fmt, const T&... args) -> int { + return vfprintf(stdout, fmt, make_printf_args(args...)); +} +template +FMT_DEPRECATED inline auto printf(basic_string_view fmt, + const T&... args) -> int { + return vfprintf(stdout, fmt, make_printf_args(args...)); +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_PRINTF_H_ diff --git a/deps/fmt/include/fmt/ranges.h b/deps/fmt/include/fmt/ranges.h new file mode 100644 index 00000000000..23ff7de0913 --- /dev/null +++ b/deps/fmt/include/fmt/ranges.h @@ -0,0 +1,850 @@ +// Formatting library for C++ - range and tuple support +// +// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_RANGES_H_ +#define FMT_RANGES_H_ + +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +FMT_EXPORT +enum class range_format { disabled, map, set, sequence, string, debug_string }; + +namespace detail { + +template class is_map { + template static auto check(U*) -> typename U::mapped_type; + template static void check(...); + + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value; +}; + +template class is_set { + template static auto check(U*) -> typename U::key_type; + template static void check(...); + + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value && !is_map::value; +}; + +// C array overload +template +auto range_begin(const T (&arr)[N]) -> const T* { + return arr; +} +template +auto range_end(const T (&arr)[N]) -> const T* { + return arr + N; +} + +template +struct has_member_fn_begin_end_t : std::false_type {}; + +template +struct has_member_fn_begin_end_t().begin()), + decltype(std::declval().end())>> + : std::true_type {}; + +// Member function overloads. +template +auto range_begin(T&& rng) -> decltype(static_cast(rng).begin()) { + return static_cast(rng).begin(); +} +template +auto range_end(T&& rng) -> decltype(static_cast(rng).end()) { + return static_cast(rng).end(); +} + +// ADL overloads. Only participate in overload resolution if member functions +// are not found. +template +auto range_begin(T&& rng) + -> enable_if_t::value, + decltype(begin(static_cast(rng)))> { + return begin(static_cast(rng)); +} +template +auto range_end(T&& rng) -> enable_if_t::value, + decltype(end(static_cast(rng)))> { + return end(static_cast(rng)); +} + +template +struct has_const_begin_end : std::false_type {}; +template +struct has_mutable_begin_end : std::false_type {}; + +template +struct has_const_begin_end< + T, void_t&>())), + decltype(detail::range_end( + std::declval&>()))>> + : std::true_type {}; + +template +struct has_mutable_begin_end< + T, void_t())), + decltype(detail::range_end(std::declval())), + // the extra int here is because older versions of MSVC don't + // SFINAE properly unless there are distinct types + int>> : std::true_type {}; + +template struct is_range_ : std::false_type {}; +template +struct is_range_ + : std::integral_constant::value || + has_mutable_begin_end::value)> {}; + +// tuple_size and tuple_element check. +template class is_tuple_like_ { + template ::type> + static auto check(U* p) -> decltype(std::tuple_size::value, 0); + template static void check(...); + + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value; +}; + +// Check for integer_sequence +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 +template +using integer_sequence = std::integer_sequence; +template using index_sequence = std::index_sequence; +template using make_index_sequence = std::make_index_sequence; +#else +template struct integer_sequence { + using value_type = T; + + static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); } +}; + +template using index_sequence = integer_sequence; + +template +struct make_integer_sequence : make_integer_sequence {}; +template +struct make_integer_sequence : integer_sequence {}; + +template +using make_index_sequence = make_integer_sequence; +#endif + +template +using tuple_index_sequence = make_index_sequence::value>; + +template ::value> +class is_tuple_formattable_ { + public: + static constexpr const bool value = false; +}; +template class is_tuple_formattable_ { + template + static auto all_true(index_sequence, + integer_sequence= 0)...>) -> std::true_type; + static auto all_true(...) -> std::false_type; + + template + static auto check(index_sequence) -> decltype(all_true( + index_sequence{}, + integer_sequence::type, + C>::value)...>{})); + + public: + static constexpr const bool value = + decltype(check(tuple_index_sequence{}))::value; +}; + +template +FMT_CONSTEXPR void for_each(index_sequence, Tuple&& t, F&& f) { + using std::get; + // Using a free function get(Tuple) now. + const int unused[] = {0, ((void)f(get(t)), 0)...}; + ignore_unused(unused); +} + +template +FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { + for_each(tuple_index_sequence>(), + std::forward(t), std::forward(f)); +} + +template +void for_each2(index_sequence, Tuple1&& t1, Tuple2&& t2, F&& f) { + using std::get; + const int unused[] = {0, ((void)f(get(t1), get(t2)), 0)...}; + ignore_unused(unused); +} + +template +void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { + for_each2(tuple_index_sequence>(), + std::forward(t1), std::forward(t2), + std::forward(f)); +} + +namespace tuple { +// Workaround a bug in MSVC 2019 (v140). +template +using result_t = std::tuple, Char>...>; + +using std::get; +template +auto get_formatters(index_sequence) + -> result_t(std::declval()))...>; +} // namespace tuple + +#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 +// Older MSVC doesn't get the reference type correctly for arrays. +template struct range_reference_type_impl { + using type = decltype(*detail::range_begin(std::declval())); +}; + +template struct range_reference_type_impl { + using type = T&; +}; + +template +using range_reference_type = typename range_reference_type_impl::type; +#else +template +using range_reference_type = + decltype(*detail::range_begin(std::declval())); +#endif + +// We don't use the Range's value_type for anything, but we do need the Range's +// reference type, with cv-ref stripped. +template +using uncvref_type = remove_cvref_t>; + +template +FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) + -> decltype(f.set_debug_format(set)) { + f.set_debug_format(set); +} +template +FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} + +template +struct range_format_kind_ + : std::integral_constant, T>::value + ? range_format::disabled + : is_map::value ? range_format::map + : is_set::value ? range_format::set + : range_format::sequence> {}; + +template +using range_format_constant = std::integral_constant; + +// These are not generic lambdas for compatibility with C++11. +template struct parse_empty_specs { + template FMT_CONSTEXPR void operator()(Formatter& f) { + f.parse(ctx); + detail::maybe_set_debug_format(f, true); + } + parse_context& ctx; +}; +template struct format_tuple_element { + using char_type = typename FormatContext::char_type; + + template + void operator()(const formatter& f, const T& v) { + if (i > 0) ctx.advance_to(detail::copy(separator, ctx.out())); + ctx.advance_to(f.format(v, ctx)); + ++i; + } + + int i; + FormatContext& ctx; + basic_string_view separator; +}; + +} // namespace detail + +template struct is_tuple_like { + static constexpr const bool value = + detail::is_tuple_like_::value && !detail::is_range_::value; +}; + +template struct is_tuple_formattable { + static constexpr const bool value = + detail::is_tuple_formattable_::value; +}; + +template +struct formatter::value && + fmt::is_tuple_formattable::value>> { + private: + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; + + basic_string_view separator_ = detail::string_literal{}; + basic_string_view opening_bracket_ = + detail::string_literal{}; + basic_string_view closing_bracket_ = + detail::string_literal{}; + + public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR void set_separator(basic_string_view sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view open, + basic_string_view close) { + opening_bracket_ = open; + closing_bracket_ = close; + } + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it != end && detail::to_ascii(*it) == 'n') { + ++it; + set_brackets({}, {}); + set_separator({}); + } + if (it != end && *it != '}') report_error("invalid format specifier"); + ctx.advance_to(it); + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + return it; + } + + template + auto format(const Tuple& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); + detail::for_each2( + formatters_, value, + detail::format_tuple_element{0, ctx, separator_}); + return detail::copy(closing_bracket_, ctx.out()); + } +}; + +template struct is_range { + static constexpr const bool value = + detail::is_range_::value && !detail::has_to_string_view::value; +}; + +namespace detail { + +template +using range_formatter_type = formatter, Char>; + +template +using maybe_const_range = + conditional_t::value, const R, R>; + +template +struct is_formattable_delayed + : is_formattable>, Char> {}; +} // namespace detail + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + +template +struct range_formatter; + +template +struct range_formatter< + T, Char, + enable_if_t>, + is_formattable>::value>> { + private: + detail::range_formatter_type underlying_; + basic_string_view separator_ = detail::string_literal{}; + basic_string_view opening_bracket_ = + detail::string_literal{}; + basic_string_view closing_bracket_ = + detail::string_literal{}; + bool is_debug = false; + + template ::value)> + auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { + auto buf = basic_memory_buffer(); + for (; it != end; ++it) buf.push_back(*it); + auto specs = format_specs(); + specs.set_type(presentation_type::debug); + return detail::write( + out, basic_string_view(buf.data(), buf.size()), specs); + } + + template ::value)> + auto write_debug_string(Output& out, It, Sentinel) const -> Output { + return out; + } + + public: + FMT_CONSTEXPR range_formatter() {} + + FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type& { + return underlying_; + } + + FMT_CONSTEXPR void set_separator(basic_string_view sep) { + separator_ = sep; + } + + FMT_CONSTEXPR void set_brackets(basic_string_view open, + basic_string_view close) { + opening_bracket_ = open; + closing_bracket_ = close; + } + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + detail::maybe_set_debug_format(underlying_, true); + if (it == end) return underlying_.parse(ctx); + + switch (detail::to_ascii(*it)) { + case 'n': + set_brackets({}, {}); + ++it; + break; + case '?': + is_debug = true; + set_brackets({}, {}); + ++it; + if (it == end || *it != 's') report_error("invalid format specifier"); + FMT_FALLTHROUGH; + case 's': + if (!std::is_same::value) + report_error("invalid format specifier"); + if (!is_debug) { + set_brackets(detail::string_literal{}, + detail::string_literal{}); + set_separator({}); + detail::maybe_set_debug_format(underlying_, false); + } + ++it; + return it; + } + + if (it != end && *it != '}') { + if (*it != ':') report_error("invalid format specifier"); + detail::maybe_set_debug_format(underlying_, false); + ++it; + } + + ctx.advance_to(it); + return underlying_.parse(ctx); + } + + template + auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { + auto out = ctx.out(); + auto it = detail::range_begin(range); + auto end = detail::range_end(range); + if (is_debug) return write_debug_string(out, std::move(it), end); + + out = detail::copy(opening_bracket_, out); + int i = 0; + for (; it != end; ++it) { + if (i > 0) out = detail::copy(separator_, out); + ctx.advance_to(out); + auto&& item = *it; // Need an lvalue + out = underlying_.format(item, ctx); + ++i; + } + out = detail::copy(closing_bracket_, out); + return out; + } +}; + +FMT_EXPORT +template +struct range_format_kind + : conditional_t< + is_range::value, detail::range_format_kind_, + std::integral_constant> {}; + +template +struct formatter< + R, Char, + enable_if_t::value != range_format::disabled && + range_format_kind::value != range_format::map && + range_format_kind::value != range_format::string && + range_format_kind::value != range_format::debug_string>, + detail::is_formattable_delayed>::value>> { + private: + using range_type = detail::maybe_const_range; + range_formatter, Char> range_formatter_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR formatter() { + if (detail::const_check(range_format_kind::value != + range_format::set)) + return; + range_formatter_.set_brackets(detail::string_literal{}, + detail::string_literal{}); + } + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return range_formatter_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + return range_formatter_.format(range, ctx); + } +}; + +// A map formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::map>, + detail::is_formattable_delayed>::value>> { + private: + using map_type = detail::maybe_const_range; + using element_type = detail::uncvref_type; + + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; + bool no_delimiters_ = false; + + public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it != end) { + if (detail::to_ascii(*it) == 'n') { + no_delimiters_ = true; + ++it; + } + if (it != end && *it != '}') { + if (*it != ':') report_error("invalid format specifier"); + ++it; + } + ctx.advance_to(it); + } + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + return it; + } + + template + auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { + auto out = ctx.out(); + basic_string_view open = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(open, out); + int i = 0; + basic_string_view sep = detail::string_literal{}; + for (auto&& value : map) { + if (i > 0) out = detail::copy(sep, out); + ctx.advance_to(out); + detail::for_each2(formatters_, value, + detail::format_tuple_element{ + 0, ctx, detail::string_literal{}}); + ++i; + } + basic_string_view close = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(close, out); + return out; + } +}; + +// A (debug_)string formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::string || + range_format_kind::value == + range_format::debug_string>> { + private: + using range_type = detail::maybe_const_range; + using string_type = + conditional_t, + decltype(detail::range_begin(std::declval())), + decltype(detail::range_end(std::declval()))>::value, + detail::std_string_view, std::basic_string>; + + formatter underlying_; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return underlying_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + out = underlying_.format( + string_type{detail::range_begin(range), detail::range_end(range)}, ctx); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + return out; + } +}; + +template +struct join_view : detail::view { + It begin; + Sentinel end; + basic_string_view sep; + + join_view(It b, Sentinel e, basic_string_view s) + : begin(std::move(b)), end(e), sep(s) {} +}; + +template +struct formatter, Char> { + private: + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t; +#else + typename std::iterator_traits::value_type; +#endif + formatter, Char> value_formatter_; + + using view = conditional_t::value, + const join_view, + join_view>; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return value_formatter_.parse(ctx); + } + + template + auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { + using iter = + conditional_t::value, It, It&>; + iter it = value.begin; + auto out = ctx.out(); + if (it == value.end) return out; + out = value_formatter_.format(*it, ctx); + ++it; + while (it != value.end) { + out = detail::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = value_formatter_.format(*it, ctx); + ++it; + } + return out; + } +}; + +template struct tuple_join_view : detail::view { + const Tuple& tuple; + basic_string_view sep; + + tuple_join_view(const Tuple& t, basic_string_view s) + : tuple(t), sep{s} {} +}; + +// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers +// support in tuple_join. It is disabled by default because of issues with +// the dynamic width and precision. +#ifndef FMT_TUPLE_JOIN_SPECIFIERS +# define FMT_TUPLE_JOIN_SPECIFIERS 0 +#endif + +template +struct formatter, Char, + enable_if_t::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, std::tuple_size()); + } + + template + auto format(const tuple_join_view& value, + FormatContext& ctx) const -> typename FormatContext::iterator { + return do_format(value, ctx, std::tuple_size()); + } + + private: + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; + + FMT_CONSTEXPR auto do_parse(parse_context& ctx, + std::integral_constant) + -> const Char* { + return ctx.begin(); + } + + template + FMT_CONSTEXPR auto do_parse(parse_context& ctx, + std::integral_constant) + -> const Char* { + auto end = ctx.begin(); +#if FMT_TUPLE_JOIN_SPECIFIERS + end = std::get::value - N>(formatters_).parse(ctx); + if (N > 1) { + auto end1 = do_parse(ctx, std::integral_constant()); + if (end != end1) + report_error("incompatible format specs for tuple elements"); + } +#endif + return end; + } + + template + auto do_format(const tuple_join_view&, FormatContext& ctx, + std::integral_constant) const -> + typename FormatContext::iterator { + return ctx.out(); + } + + template + auto do_format(const tuple_join_view& value, FormatContext& ctx, + std::integral_constant) const -> + typename FormatContext::iterator { + using std::get; + auto out = + std::get::value - N>(formatters_) + .format(get::value - N>(value.tuple), ctx); + if (N <= 1) return out; + out = detail::copy(value.sep, out); + ctx.advance_to(out); + return do_format(value, ctx, std::integral_constant()); + } +}; + +namespace detail { +// Check if T has an interface like a container adaptor (e.g. std::stack, +// std::queue, std::priority_queue). +template class is_container_adaptor_like { + template static auto check(U* p) -> typename U::container_type; + template static void check(...); + + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value; +}; + +template struct all { + const Container& c; + auto begin() const -> typename Container::const_iterator { return c.begin(); } + auto end() const -> typename Container::const_iterator { return c.end(); } +}; +} // namespace detail + +template +struct formatter< + T, Char, + enable_if_t, + bool_constant::value == + range_format::disabled>>::value>> + : formatter, Char> { + using all = detail::all; + template + auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { + struct getter : T { + static auto get(const T& v) -> all { + return {v.*(&getter::c)}; // Access c through the derived class. + } + }; + return formatter::format(getter::get(value), ctx); + } +}; + +FMT_BEGIN_EXPORT + +/// Returns a view that formats the iterator range `[begin, end)` with elements +/// separated by `sep`. +template +auto join(It begin, Sentinel end, string_view sep) -> join_view { + return {std::move(begin), end, sep}; +} + +/** + * Returns a view that formats `range` with elements separated by `sep`. + * + * **Example**: + * + * auto v = std::vector{1, 2, 3}; + * fmt::print("{}", fmt::join(v, ", ")); + * // Output: 1, 2, 3 + * + * `fmt::join` applies passed format specifiers to the range elements: + * + * fmt::print("{:02}", fmt::join(v, ", ")); + * // Output: 01, 02, 03 + */ +template ::value)> +auto join(Range&& r, string_view sep) + -> join_view { + return {detail::range_begin(r), detail::range_end(r), sep}; +} + +/** + * Returns an object that formats `std::tuple` with elements separated by `sep`. + * + * **Example**: + * + * auto t = std::tuple{1, 'a'}; + * fmt::print("{}", fmt::join(t, ", ")); + * // Output: 1, a + */ +template ::value)> +FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) + -> tuple_join_view { + return {tuple, sep}; +} + +/** + * Returns an object that formats `std::initializer_list` with elements + * separated by `sep`. + * + * **Example**: + * + * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); + * // Output: "1, 2, 3" + */ +template +auto join(std::initializer_list list, string_view sep) + -> join_view { + return join(std::begin(list), std::end(list), sep); +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_RANGES_H_ diff --git a/deps/fmt/include/fmt/std.h b/deps/fmt/include/fmt/std.h new file mode 100644 index 00000000000..f43dc74d21c --- /dev/null +++ b/deps/fmt/include/fmt/std.h @@ -0,0 +1,728 @@ +// Formatting library for C++ - formatters for standard library types +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_STD_H_ +#define FMT_STD_H_ + +#include "format.h" +#include "ostream.h" + +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. +# if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE() && \ + (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +# endif +// Use > instead of >= in the version check because may be +// available after C++17 but before C++20 is marked as implemented. +# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() +# include +# endif +# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() +# include +# endif +#endif // FMT_MODULE + +#if FMT_HAS_INCLUDE() +# include +#endif + +// GCC 4 does not support FMT_HAS_INCLUDE. +#if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) +# include +// Android NDK with gabi++ library on some architectures does not implement +// abi::__cxa_demangle(). +# ifndef __GABIXX_CXXABI_H__ +# define FMT_HAS_ABI_CXA_DEMANGLE +# endif +#endif + +// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. +#ifndef FMT_CPP_LIB_FILESYSTEM +# ifdef __cpp_lib_filesystem +# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem +# else +# define FMT_CPP_LIB_FILESYSTEM 0 +# endif +#endif + +#ifndef FMT_CPP_LIB_VARIANT +# ifdef __cpp_lib_variant +# define FMT_CPP_LIB_VARIANT __cpp_lib_variant +# else +# define FMT_CPP_LIB_VARIANT 0 +# endif +#endif + +#if FMT_CPP_LIB_FILESYSTEM +FMT_BEGIN_NAMESPACE + +namespace detail { + +template +auto get_path_string(const std::filesystem::path& p, + const std::basic_string& native) { + if constexpr (std::is_same_v && std::is_same_v) + return to_utf8(native, to_utf8_error_policy::replace); + else + return p.string(); +} + +template +void write_escaped_path(basic_memory_buffer& quoted, + const std::filesystem::path& p, + const std::basic_string& native) { + if constexpr (std::is_same_v && + std::is_same_v) { + auto buf = basic_memory_buffer(); + write_escaped_string(std::back_inserter(buf), native); + bool valid = to_utf8::convert(quoted, {buf.data(), buf.size()}); + FMT_ASSERT(valid, "invalid utf16"); + } else if constexpr (std::is_same_v) { + write_escaped_string( + std::back_inserter(quoted), native); + } else { + write_escaped_string(std::back_inserter(quoted), p.string()); + } +} + +} // namespace detail + +template struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + bool debug_ = false; + char path_type_ = 0; + + public: + FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } + + FMT_CONSTEXPR auto parse(parse_context& ctx) { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + + it = detail::parse_align(it, end, specs_); + if (it == end) return it; + + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it != end && *it == '?') { + debug_ = true; + ++it; + } + if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); + return it; + } + + template + auto format(const std::filesystem::path& p, FormatContext& ctx) const { + auto specs = specs_; + auto path_string = + !path_type_ ? p.native() + : p.generic_string(); + + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + if (!debug_) { + auto s = detail::get_path_string(p, path_string); + return detail::write(ctx.out(), basic_string_view(s), specs); + } + auto quoted = basic_memory_buffer(); + detail::write_escaped_path(quoted, p, path_string); + return detail::write(ctx.out(), + basic_string_view(quoted.data(), quoted.size()), + specs); + } +}; + +class path : public std::filesystem::path { + public: + auto display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{}"), base); + } + auto system_string() const -> std::string { return string(); } + + auto generic_display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{:g}"), base); + } + auto generic_system_string() const -> std::string { return generic_string(); } +}; + +FMT_END_NAMESPACE +#endif // FMT_CPP_LIB_FILESYSTEM + +FMT_BEGIN_NAMESPACE +template +struct formatter, Char> + : nested_formatter, Char> { + private: + // Functor because C++11 doesn't support generic lambdas. + struct writer { + const std::bitset& bs; + + template + FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { + for (auto pos = N; pos > 0; --pos) { + out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); + } + + return out; + } + }; + + public: + template + auto format(const std::bitset& bs, FormatContext& ctx) const + -> decltype(ctx.out()) { + return this->write_padded(ctx, writer{bs}); + } +}; + +template +struct formatter : basic_ostream_formatter {}; +FMT_END_NAMESPACE + +#ifdef __cpp_lib_optional +FMT_BEGIN_NAMESPACE +template +struct formatter, Char, + std::enable_if_t::value>> { + private: + formatter underlying_; + static constexpr basic_string_view optional = + detail::string_literal{}; + static constexpr basic_string_view none = + detail::string_literal{}; + + template + FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set) + -> decltype(u.set_debug_format(set)) { + u.set_debug_format(set); + } + + template + FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) { + maybe_set_debug_format(underlying_, true); + return underlying_.parse(ctx); + } + + template + auto format(const std::optional& opt, FormatContext& ctx) const + -> decltype(ctx.out()) { + if (!opt) return detail::write(ctx.out(), none); + + auto out = ctx.out(); + out = detail::write(out, optional); + ctx.advance_to(out); + out = underlying_.format(*opt, ctx); + return detail::write(out, ')'); + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_optional + +#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (has_to_string_view::value) + return write_escaped_string(out, detail::to_string_view(v)); + if constexpr (std::is_same_v) return write_escaped_char(out, v); + return write(out, v); +} + +} // namespace detail + +FMT_END_NAMESPACE +#endif + +#ifdef __cpp_lib_expected +FMT_BEGIN_NAMESPACE + +template +struct formatter, Char, + std::enable_if_t<(std::is_void::value || + is_formattable::value) && + is_formattable::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::expected& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + if (value.has_value()) { + out = detail::write(out, "expected("); + if constexpr (!std::is_void::value) + out = detail::write_escaped_alternative(out, *value); + } else { + out = detail::write(out, "unexpected("); + out = detail::write_escaped_alternative(out, value.error()); + } + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_expected + +#ifdef __cpp_lib_source_location +FMT_BEGIN_NAMESPACE +template <> struct formatter { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } + + template + auto format(const std::source_location& loc, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write(out, loc.file_name()); + out = detail::write(out, ':'); + out = detail::write(out, loc.line()); + out = detail::write(out, ':'); + out = detail::write(out, loc.column()); + out = detail::write(out, ": "); + out = detail::write(out, loc.function_name()); + return out; + } +}; +FMT_END_NAMESPACE +#endif + +#if FMT_CPP_LIB_VARIANT +FMT_BEGIN_NAMESPACE +namespace detail { + +template +using variant_index_sequence = + std::make_index_sequence::value>; + +template struct is_variant_like_ : std::false_type {}; +template +struct is_variant_like_> : std::true_type {}; + +// formattable element check. +template class is_variant_formattable_ { + template + static std::conjunction< + is_formattable, C>...> + check(std::index_sequence); + + public: + static constexpr const bool value = + decltype(check(variant_index_sequence{}))::value; +}; + +} // namespace detail + +template struct is_variant_like { + static constexpr const bool value = detail::is_variant_like_::value; +}; + +template struct is_variant_formattable { + static constexpr const bool value = + detail::is_variant_formattable_::value; +}; + +template struct formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::monostate&, FormatContext& ctx) const + -> decltype(ctx.out()) { + return detail::write(ctx.out(), "monostate"); + } +}; + +template +struct formatter< + Variant, Char, + std::enable_if_t, is_variant_formattable>>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const Variant& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + out = detail::write(out, "variant("); + FMT_TRY { + std::visit( + [&](const auto& v) { + out = detail::write_escaped_alternative(out, v); + }, + value); + } + FMT_CATCH(const std::bad_variant_access&) { + detail::write(out, "valueless by exception"); + } + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif // FMT_CPP_LIB_VARIANT + +FMT_BEGIN_NAMESPACE +template <> struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + bool debug_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + + it = detail::parse_align(it, end, specs_); + + char c = *it; + if (it != end && ((c >= '0' && c <= '9') || c == '{')) + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + + if (it != end && *it == '?') { + debug_ = true; + ++it; + } + if (it != end && *it == 's') { + specs_.set_type(presentation_type::string); + ++it; + } + return it; + } + + template + FMT_CONSTEXPR20 auto format(const std::error_code& ec, + FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + auto buf = memory_buffer(); + if (specs_.type() == presentation_type::string) { + buf.append(ec.message()); + } else { + buf.append(string_view(ec.category().name())); + buf.push_back(':'); + detail::write(appender(buf), ec.value()); + } + auto quoted = memory_buffer(); + auto str = string_view(buf.data(), buf.size()); + if (debug_) { + detail::write_escaped_string(std::back_inserter(quoted), str); + str = string_view(quoted.data(), quoted.size()); + } + return detail::write(ctx.out(), str, specs); + } +}; + +#if FMT_USE_RTTI +namespace detail { + +template +auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { +# ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + std::size_t size = 0; + std::unique_ptr demangled_name_ptr( + abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); + + string_view demangled_name_view; + if (demangled_name_ptr) { + demangled_name_view = demangled_name_ptr.get(); + + // Normalization of stdlib inline namespace names. + // libc++ inline namespaces. + // std::__1::* -> std::* + // std::__1::__fs::* -> std::* + // libstdc++ inline namespaces. + // std::__cxx11::* -> std::* + // std::filesystem::__cxx11::* -> std::filesystem::* + if (demangled_name_view.starts_with("std::")) { + char* begin = demangled_name_ptr.get(); + char* to = begin + 5; // std:: + for (char *from = to, *end = begin + demangled_name_view.size(); + from < end;) { + // This is safe, because demangled_name is NUL-terminated. + if (from[0] == '_' && from[1] == '_') { + char* next = from + 1; + while (next < end && *next != ':') next++; + if (next[0] == ':' && next[1] == ':') { + from = next + 2; + continue; + } + } + *to++ = *from++; + } + demangled_name_view = {begin, detail::to_unsigned(to - begin)}; + } + } else { + demangled_name_view = string_view(ti.name()); + } + return detail::write_bytes(out, demangled_name_view); +# elif FMT_MSC_VERSION + const string_view demangled_name(ti.name()); + for (std::size_t i = 0; i < demangled_name.size(); ++i) { + auto sub = demangled_name; + sub.remove_prefix(i); + if (sub.starts_with("enum ")) { + i += 4; + continue; + } + if (sub.starts_with("class ") || sub.starts_with("union ")) { + i += 5; + continue; + } + if (sub.starts_with("struct ")) { + i += 6; + continue; + } + if (*sub.begin() != ' ') *out++ = *sub.begin(); + } + return out; +# else + return detail::write_bytes(out, string_view(ti.name())); +# endif +} + +} // namespace detail + +template +struct formatter { + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::type_info& ti, Context& ctx) const + -> decltype(ctx.out()) { + return detail::write_demangled_name(ctx.out(), ti); + } +}; +#endif + +template +struct formatter< + T, Char, // DEPRECATED! Mixing code unit types. + typename std::enable_if::value>::type> { + private: + bool with_typename_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it == end || *it == '}') return it; + if (*it == 't') { + ++it; + with_typename_ = FMT_USE_RTTI != 0; + } + return it; + } + + template + auto format(const std::exception& ex, Context& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); +#if FMT_USE_RTTI + if (with_typename_) { + out = detail::write_demangled_name(out, typeid(ex)); + *out++ = ':'; + *out++ = ' '; + } +#endif + return detail::write_bytes(out, string_view(ex.what())); + } +}; + +namespace detail { + +template +struct has_flip : std::false_type {}; + +template +struct has_flip().flip())>> + : std::true_type {}; + +template struct is_bit_reference_like { + static constexpr const bool value = + std::is_convertible::value && + std::is_nothrow_assignable::value && has_flip::value; +}; + +#ifdef _LIBCPP_VERSION + +// Workaround for libc++ incompatibility with C++ standard. +// According to the Standard, `bitset::operator[] const` returns bool. +template +struct is_bit_reference_like> { + static constexpr const bool value = true; +}; + +#endif + +} // namespace detail + +// We can't use std::vector::reference and +// std::bitset::reference because the compiler can't deduce Allocator and N +// in partial specialization. +template +struct formatter::value>> + : formatter { + template + FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter::format(v, ctx); + } +}; + +template +auto ptr(const std::unique_ptr& p) -> const void* { + return p.get(); +} +template auto ptr(const std::shared_ptr& p) -> const void* { + return p.get(); +} + +template +struct formatter, Char, + enable_if_t::value>> + : formatter { + template + auto format(const std::atomic& v, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter::format(v.load(), ctx); + } +}; + +#ifdef __cpp_lib_atomic_flag_test +template +struct formatter : formatter { + template + auto format(const std::atomic_flag& v, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter::format(v.test(), ctx); + } +}; +#endif // __cpp_lib_atomic_flag_test + +template struct formatter, Char> { + private: + detail::dynamic_format_specs specs_; + + template + FMT_CONSTEXPR auto do_format(const std::complex& c, + detail::dynamic_format_specs& specs, + FormatContext& ctx, OutputIt out) const + -> OutputIt { + if (c.real() != 0) { + *out++ = Char('('); + out = detail::write(out, c.real(), specs, ctx.locale()); + specs.set_sign(sign::plus); + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + *out++ = Char(')'); + return out; + } + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + return out; + } + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type_constant::value); + } + + template + auto format(const std::complex& c, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs = specs_; + if (specs.dynamic()) { + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); + auto buf = basic_memory_buffer(); + + auto outer_specs = format_specs(); + outer_specs.width = specs.width; + outer_specs.copy_fill_from(specs); + outer_specs.set_align(specs.align()); + + specs.width = 0; + specs.set_fill({}); + specs.set_align(align::none); + + do_format(c, specs, ctx, basic_appender(buf)); + return detail::write(ctx.out(), + basic_string_view(buf.data(), buf.size()), + outer_specs); + } +}; + +template +struct formatter, Char, + enable_if_t, Char>::value>> + : formatter, Char> { + template + auto format(std::reference_wrapper ref, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format(ref.get(), ctx); + } +}; + +FMT_END_NAMESPACE +#endif // FMT_STD_H_ diff --git a/deps/fmt/include/fmt/xchar.h b/deps/fmt/include/fmt/xchar.h new file mode 100644 index 00000000000..90994fff6e2 --- /dev/null +++ b/deps/fmt/include/fmt/xchar.h @@ -0,0 +1,369 @@ +// Formatting library for C++ - optional wchar_t and exotic character support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_XCHAR_H_ +#define FMT_XCHAR_H_ + +#include "color.h" +#include "format.h" +#include "ostream.h" +#include "ranges.h" + +#ifndef FMT_MODULE +# include +# if FMT_USE_LOCALE +# include +# endif +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +using is_exotic_char = bool_constant::value>; + +template struct format_string_char {}; + +template +struct format_string_char< + S, void_t())))>> { + using type = char_t; +}; + +template +struct format_string_char< + S, enable_if_t::value>> { + using type = typename S::char_type; +}; + +template +using format_string_char_t = typename format_string_char::type; + +inline auto write_loc(basic_appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool { +#if FMT_USE_LOCALE + auto& numpunct = + std::use_facet>(loc.get()); + auto separator = std::wstring(); + auto grouping = numpunct.grouping(); + if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); + return value.visit(loc_writer{out, specs, separator, grouping, {}}); +#endif + return false; +} +} // namespace detail + +FMT_BEGIN_EXPORT + +using wstring_view = basic_string_view; +using wformat_parse_context = parse_context; +using wformat_context = buffered_context; +using wformat_args = basic_format_args; +using wmemory_buffer = basic_memory_buffer; + +template struct basic_fstring { + private: + basic_string_view str_; + + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + Char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + using t = basic_fstring; + + template >::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(s, checker(s, arg_pack())); + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { + FMT_CONSTEXPR auto sv = basic_string_view(S()); + FMT_CONSTEXPR int ignore = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(ignore); + } + basic_fstring(runtime_format_string fmt) : str_(fmt.str) {} + + operator basic_string_view() const { return str_; } + auto get() const -> basic_string_view { return str_; } +}; + +template +using basic_format_string = basic_fstring; + +template +using wformat_string = typename basic_format_string::t; +inline auto runtime(wstring_view s) -> runtime_format_string { + return {{s}}; +} + +#ifdef __cpp_char8_t +template <> struct is_char : bool_constant {}; +#endif + +template +constexpr auto make_wformat_args(T&... args) + -> decltype(fmt::make_format_args(args...)) { + return fmt::make_format_args(args...); +} + +#if !FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg { + return {s}; +} +} // namespace literals +#endif + +template +auto join(It begin, Sentinel end, wstring_view sep) + -> join_view { + return {begin, end, sep}; +} + +template ::value)> +auto join(Range&& range, wstring_view sep) + -> join_view { + return join(std::begin(range), std::end(range), sep); +} + +template +auto join(std::initializer_list list, wstring_view sep) + -> join_view { + return join(std::begin(list), std::end(list), sep); +} + +template ::value)> +auto join(const Tuple& tuple, basic_string_view sep) + -> tuple_join_view { + return {tuple, sep}; +} + +template ::value)> +auto vformat(basic_string_view fmt, + typename detail::vformat_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vformat_to(buf, fmt, args); + return {buf.data(), buf.size()}; +} + +template +auto format(wformat_string fmt, T&&... args) -> std::wstring { + return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template +auto format_to(OutputIt out, wformat_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt::wstring_view(fmt), + fmt::make_wformat_args(args...)); +} + +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template , + FMT_ENABLE_IF(!std::is_same::value && + !std::is_same::value)> +auto format(const S& fmt, T&&... args) -> std::basic_string { + return vformat(detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto vformat(const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vformat_to(buf, detail::to_string_view(fmt), args, + detail::locale_ref(loc)); + return {buf.data(), buf.size()}; +} + +template , + FMT_ENABLE_IF(detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto format(const Locale& loc, const S& fmt, T&&... args) + -> std::basic_string { + return vformat(loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_exotic_char::value)> +auto vformat_to(OutputIt out, const S& fmt, + typename detail::vformat_args::type args) -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, detail::to_string_view(fmt), args); + return detail::get_iterator(buf, out); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value && + !std::is_same::value && + !std::is_same::value)> +inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { + return vformat_to(out, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) + -> OutputIt { + auto&& buf = detail::get_buffer(out); + vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); + return detail::get_iterator(buf, out); +} + +template , + bool enable = detail::is_output_iterator::value && + detail::is_locale::value && + detail::is_exotic_char::value> +inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, + T&&... args) -> + typename std::enable_if::type { + return vformat_to(out, loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template ::value&& + detail::is_exotic_char::value)> +inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, + typename detail::vformat_args::type args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args); + return {buf.out(), buf.count()}; +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_exotic_char::value)> +inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) + -> format_to_n_result { + return vformat_to_n(out, n, fmt::basic_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_exotic_char::value)> +inline auto formatted_size(const S& fmt, T&&... args) -> size_t { + auto buf = detail::counting_buffer(); + detail::vformat_to(buf, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); + return buf.count(); +} + +inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { + auto buf = wmemory_buffer(); + detail::vformat_to(buf, fmt, args); + buf.push_back(L'\0'); + if (std::fputws(buf.data(), f) == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +inline void vprint(wstring_view fmt, wformat_args args) { + vprint(stdout, fmt, args); +} + +template +void print(std::FILE* f, wformat_string fmt, T&&... args) { + return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template void print(wformat_string fmt, T&&... args) { + return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template +void println(std::FILE* f, wformat_string fmt, T&&... args) { + return print(f, L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +template void println(wformat_string fmt, T&&... args) { + return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) + -> std::wstring { + auto buf = wmemory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + return {buf.data(), buf.size()}; +} + +template +inline auto format(text_style ts, wformat_string fmt, T&&... args) + -> std::wstring { + return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string fmt, + const T&... args) { + vprint(f, ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(text_style ts, wformat_string fmt, + const T&... args) { + return print(stdout, ts, fmt, args...); +} + +inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { + auto buffer = basic_memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::write_buffer(os, buffer); +} + +template +void print(std::wostream& os, wformat_string fmt, T&&... args) { + vprint(os, fmt, fmt::make_format_args>(args...)); +} + +template +void println(std::wostream& os, wformat_string fmt, T&&... args) { + print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +/// Converts `value` to `std::wstring` using the default format for type `T`. +template inline auto to_wstring(const T& value) -> std::wstring { + return format(FMT_STRING(L"{}"), value); +} +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_XCHAR_H_ diff --git a/deps/jquery/Makefile b/deps/jquery/Makefile index 35fd593b125..55a6760e6b1 100644 --- a/deps/jquery/Makefile +++ b/deps/jquery/Makefile @@ -5,7 +5,8 @@ POWERTIP_VERSION = 1.3.1 TOUCHPUNCH_VERSION = 0.2.3 SMARTMENUS_VERSION = 1.1.0 -MINIFIER ?= /usr/local/bin/yuicompressor-2.4.8 +UGLIFYCSS ?= /usr/local/bin/uglifycss +UGLIFYJS ?= /usr/local/bin/uglifyjs SCRIPTS = jquery-$(JQUERY_VERSION).min.js \ jquery.ui-$(JQUERY_UI_VERSION).min.js \ jquery.scrollTo-$(SCROLL_VERSION).min.js \ @@ -30,7 +31,9 @@ doxmenu-min.css: sm-core-css.css \ sass/_sub-items-indentation.scss compass compile --css-dir . --force sass/sm-dox.scss cat sm-core-css.css sm-dox.css > doxmenu.css - java -jar $(MINIFIER).jar doxmenu.css > doxmenu-min.css + $(UGLIFYCSS) doxmenu.css > doxmenu-min.css + $(UGLIFYJS) -O max_line_len=1024,comments=true jquery.js > jquery-min.js + mv jquery-min.js jquery.js rm -f sm-dox.css doxmenu.css clean: diff --git a/deps/jquery/sass/_sm-dox.scss b/deps/jquery/sass/_sm-dox.scss index 1ada84dc2a3..47497f3daa7 100644 --- a/deps/jquery/sass/_sm-dox.scss +++ b/deps/jquery/sass/_sm-dox.scss @@ -53,7 +53,7 @@ $sm-dox__border-radius: 5px !default; // Menu box //$sm-dox__collapsible-bg: $sm-dox__gray !default; -$sm-dox__collapsible-bg: var(--nav-gradient-image) !default; +$sm-dox__collapsible-bg: var(--nav-menu-background-color) !default; $sm-dox__collapsible-border-radius: $sm-dox__border-radius !default; // Items @@ -87,7 +87,7 @@ $sm-dox__collapsible-sub-item-indentation: 8px !default; // Menu box //$sm-dox__desktop-bg: $sm-dox__gray !default; -$sm-dox__desktop-bg: var(--nav-gradient-image) !default; +$sm-dox__desktop-bg: var(--nav-menu-background-color) !default; //$sm-dox__desktop-border-radius: 100px !default; $sm-dox__desktop-padding-horizontal: 10px !default; @@ -97,12 +97,12 @@ $sm-dox__desktop-item-hover-color: var(--nav-text-hover-color $sm-dox__desktop-item-current-color: var(--nav-text-disabled-color) !default; //$sm-dox__red !default; $sm-dox__desktop-item-disabled-color: darken($sm-dox__gray, 20%) !default; $sm-dox__desktop-item-padding-vertical: 0px !default; -$sm-dox__desktop-item-padding-horizontal: 12px !default; +$sm-dox__desktop-item-padding-horizontal: 6px !default; // Sub menu indicators $sm-dox__desktop-arrow-size: 4px !default; // border-width $sm-dox__desktop-arrow-color: $sm-dox__gray-darker !default; -$sm-dox__desktop-arrow-spacing: 4px !default; +$sm-dox__desktop-arrow-spacing: 10px !default; // Vertical menu box $sm-dox__desktop-vertical-border-radius: $sm-dox__border-radius !default; @@ -163,7 +163,7 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); // Main menu box .sm-dox { - background-image: $sm-dox__collapsible-bg; + background-color: $sm-dox__collapsible-bg; //@include border-radius($sm-dox__collapsible-border-radius); // Main menu items @@ -175,22 +175,17 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); padding: $sm-dox__collapsible-item-padding-vertical $sm-dox__collapsible-item-padding-horizontal; /* make room for the toggle button (sub indicator) */ padding-right: $sm-dox__collapsible-item-padding-horizontal + $sm-dox__toggle-size + $sm-dox__toggle-spacing; - /* color: $sm-dox__collapsible-item-color; */ font-family: $sm-dox__font-family; font-size: $sm-dox__font-size-base; - font-weight: bold; - line-height: 36px; //$sm-dox__line-height; + line-height: 36px; text-decoration: none; - text-shadow: var(--nav-text-normal-shadow); color: $sm-dox__main-text-color; outline: none; } &:hover { - background-image: var(--nav-gradient-active-image); - background-repeat:repeat-x; - color: $sm-dox__main-highlight-color; - text-shadow: var(--nav-text-hover-shadow); + background-color: var(--nav-menu-active-bg); + border-radius: 5px; } &.current { @@ -214,7 +209,6 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); font: bold #{$sm-dox__font-size-small}/#{$sm-dox__toggle-size} monospace !important; text-align: center; text-shadow: none; - background: $sm-dox__collapsible-toggle-bg; @include border-radius($sm-dox__border-radius); } & span.sub-arrow:before { @@ -265,12 +259,8 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); } &:hover { - // color: $sm-dox__collapsible-item-current-color; - // background-color: $sm-dox__gray; - background-image: var(--nav-gradient-active-image); - background-repeat:repeat-x; - color: $sm-dox__main-highlight-color; - text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); + background-color: var(--nav-menu-active-bg); + border-radius: 5px; } } @@ -305,23 +295,27 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); // Main menu box .sm-dox { padding: 0 $sm-dox__desktop-padding-horizontal; - background-image: $sm-dox__desktop-bg; + background-color: $sm-dox__desktop-bg; line-height: 36px; + //border-bottom: 1px solid var(--nav-border-color); //@include border-radius($sm-dox__desktop-border-radius); // Main menu items a { // Sub menu indicators span.sub-arrow { - top: 50%; - margin-top: -(ceil($sm-dox__desktop-arrow-size / 2)); - right: $sm-dox__desktop-item-padding-horizontal; - width: 0; - height: 0; - border-width: $sm-dox__desktop-arrow-size; - border-style: solid dashed dashed dashed; - border-color: $sm-dox__main-text-color transparent transparent transparent; - background: transparent; + top: 15px; + right: 10px; + box-sizing: content-box; + padding: 0; + margin: 0; + display: inline-block; + width: 5px; + height: 5px; + background-color: var(--nav-menu-background-color); + border-right: 2px solid var(--nav-arrow-color); + border-bottom: 2px solid var(--nav-arrow-color); + transform: rotate(45deg); @include border-radius(0); } @@ -331,20 +325,16 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); &:hover, &.highlighted { padding: $sm-dox__desktop-item-padding-vertical $sm-dox__desktop-item-padding-horizontal; - /*color: $sm-dox__desktop-item-color;*/ - background-image:var(--nav-separator-image); - background-repeat:no-repeat; - background-position:right; - @include border-radius(0 !important); } &:hover { - background-image: var(--nav-gradient-active-image); - background-repeat:repeat-x; - color: $sm-dox__main-highlight-color; - text-shadow: var(--nav-text-hover-shadow); - span.sub-arrow { - border-color: $sm-dox__main-highlight-color transparent transparent transparent; - } + background-color: var(--nav-menu-active-bg); + border-radius: 5px !important; + } + + &:hover span.sub-arrow { + background-color: var(--nav-menu-active-bg); + border-right: 2px solid var(--nav-arrow-selected-color); + border-bottom: 2px solid var(--nav-arrow-selected-color); } // Make room for the sub arrows @@ -356,6 +346,7 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); // No main menu items separators li { border-top: 0; + padding: 3px; } // First sub level carets @@ -390,12 +381,7 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); // Sub menus items a { span.sub-arrow { - right: 8px; - top: 50%; - margin-top: -$sm-dox__desktop-sub-arrow-size; - border-width: $sm-dox__desktop-sub-arrow-size; - border-color: transparent transparent transparent $sm-dox__desktop-sub-item-color; - border-style: dashed dashed dashed solid; + transform: rotate(-45deg); } &, @@ -409,13 +395,8 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); } &:hover { - background-image: var(--nav-gradient-active-image); - background-repeat:repeat-x; - color: $sm-dox__main-highlight-color; - text-shadow: var(--nav-text-hover-shadow); - span.sub-arrow { - border-color: transparent transparent transparent $sm-dox__main-highlight-color; - } + background-color: var(--nav-menu-active-bg); + border-radius: 5px; } } } @@ -552,7 +533,7 @@ $sm-dox__toggle-spacing: floor($sm-dox__main-row-height * 0.1); } &.disabled { - background-image: $sm-dox__desktop-bg; + //background-image: $sm-dox__desktop-bg; } // Sub menu indicators diff --git a/deps/libmd5/md5.c b/deps/libmd5/md5.c index bcf89802db2..338c0cc515d 100644 --- a/deps/libmd5/md5.c +++ b/deps/libmd5/md5.c @@ -37,8 +37,8 @@ detectEndianess() int nl = 0x12345678; short ns = 0x1234; - unsigned char *p = (unsigned char *)(&nl); - unsigned char *sp = (unsigned char *)(&ns); + const unsigned char *p = (unsigned char *)(&nl); + const unsigned char *sp = (unsigned char *)(&ns); if (g_endianessDetected) return; if ( p[0] == 0x12 && p[1] == 0x34 && p[2] == 0x56 && p[3] == 0x78 ) @@ -60,11 +60,9 @@ detectEndianess() static void byteSwap(UWORD32 *buf, unsigned words) { - md5byte *p; - if (!g_bigEndian) return; - p = (md5byte *)buf; + md5byte *p = (md5byte *)buf; do { *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | @@ -98,11 +96,9 @@ MD5Init(struct MD5Context *ctx) void MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) { - UWORD32 t; - /* Update byte count */ - t = ctx->bytes[0]; + UWORD32 t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) ctx->bytes[1]++; /* Carry from low to high */ @@ -189,12 +185,10 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) { - UWORD32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; + UWORD32 a = buf[0]; + UWORD32 b = buf[1]; + UWORD32 c = buf[2]; + UWORD32 d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); @@ -284,18 +278,14 @@ void MD5Buffer (const char *buf,unsigned int len,unsigned char sig[16]) void MD5SigToString(unsigned char signature[16],char str[33]) { - unsigned char *sig_p; - char *str_p; - unsigned int high, low; + char *str_p = str; - str_p = str; - - for (sig_p = (unsigned char *)signature; + for (unsigned char *sig_p = (unsigned char *)signature; sig_p < (unsigned char *)signature + 16; sig_p++) { - high = *sig_p / 16; - low = *sig_p % 16; + unsigned int high = *sig_p / 16; + unsigned int low = *sig_p % 16; /* account for 2 chars */ *str_p++ = HEX_STRING[high]; *str_p++ = HEX_STRING[low]; @@ -303,5 +293,3 @@ void MD5SigToString(unsigned char signature[16],char str[33]) /* account for 1 terminator */ *str_p++ = '\0'; } - - diff --git a/deps/libmscgen/gd.c b/deps/libmscgen/gd.c index a9a8aa68aad..dc25a864d4a 100644 --- a/deps/libmscgen/gd.c +++ b/deps/libmscgen/gd.c @@ -1110,45 +1110,43 @@ BGD_DECLARE(int) gdImageColorReplaceArray (gdImagePtr im, int len, int *src, int */ BGD_DECLARE(int) gdImageColorReplaceCallback (gdImagePtr im, gdCallbackImageColor callback) { - int c, d, n = 0; + int n = 0; if (!callback) { return 0; } if (im->trueColor) { - register int x, y; - - for (y = im->cy1; y <= im->cy2; y++) { - for (x = im->cx1; x <= im->cx2; x++) { - c = gdImageTrueColorPixel(im, x, y); - if ( (d = callback(im, c)) != c) { + for (int y = im->cy1; y <= im->cy2; y++) { + for (int x = im->cx1; x <= im->cx2; x++) { + int c = gdImageTrueColorPixel(im, x, y); + int d = callback(im, c); + if (d != c) { gdImageSetPixel(im, x, y, d); n++; } } } } else { /* palette */ - int *sarr, *darr; - int k, len = 0; + int len = 0; - sarr = (int *)gdCalloc(im->colorsTotal, sizeof(int)); + int *sarr = (int *)gdCalloc(im->colorsTotal, sizeof(int)); if (!sarr) { return -1; } - for (c = 0; c < im->colorsTotal; c++) { + for (int c = 0; c < im->colorsTotal; c++) { if (!im->open[c]) { sarr[len++] = c; } } - darr = (int *)gdCalloc(len, sizeof(int)); + int *darr = (int *)gdCalloc(len, sizeof(int)); if (!darr) { gdFree(sarr); return -1; } - for (k = 0; k < len; k++) { + for (int k = 0; k < len; k++) { darr[k] = callback(im, sarr[k]); } - n = gdImageColorReplaceArray(im, k, sarr, darr); + n = gdImageColorReplaceArray(im, len, sarr, darr); gdFree(darr); gdFree(sarr); } @@ -1719,30 +1717,31 @@ static void dashedSet (gdImagePtr im, int x, int y, int color, */ BGD_DECLARE(void) gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) { - int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag; + int x, y; int dashStep = 0; int on = 1; int wid; int vert; - int thick = im->thick; - dx = abs (x2 - x1); - dy = abs (y2 - y1); + const int dx = abs (x2 - x1); + const int dy = abs (y2 - y1); if (dy <= dx) { /* More-or-less horizontal. use wid for vertical stroke */ /* 2.0.12: Michael Schwartz: divide rather than multiply; TBB: but watch out for /0! */ double as = sin (atan2 (dy, dx)); if (as != 0) { - wid = (int)(thick / as); + wid = (int)(im->thick / as); } else { wid = 1; } vert = 1; - d = 2 * dy - dx; - incr1 = 2 * dy; - incr2 = 2 * (dy - dx); + int d = 2 * dy - dx; + const int incr1 = 2 * dy; + const int incr2 = 2 * (dy - dx); + int xend; + int ydirflag; if (x1 > x2) { x = x2; y = y2; @@ -1783,15 +1782,17 @@ BGD_DECLARE(void) gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int TBB: but watch out for /0! */ double as = sin (atan2 (dy, dx)); if (as != 0) { - wid = (int)(thick / as); + wid = (int)(im->thick / as); } else { wid = 1; } vert = 0; - d = 2 * dx - dy; - incr1 = 2 * dx; - incr2 = 2 * (dx - dy); + int d = 2 * dx - dy; + const int incr1 = 2 * dx; + const int incr2 = 2 * (dx - dy); + int yend; + int xdirflag; if (y1 > y2) { y = y2; x = x2; @@ -1836,7 +1837,6 @@ dashedSet (gdImagePtr im, int x, int y, int color, { int dashStep = *dashStepP; int on = *onP; - int w, wstart; dashStep++; if (dashStep == gdDashSize) { @@ -1845,12 +1845,12 @@ dashedSet (gdImagePtr im, int x, int y, int color, } if (on) { if (vert) { - wstart = y - wid / 2; - for (w = wstart; w < wstart + wid; w++) + int wstart = y - wid / 2; + for (int w = wstart; w < wstart + wid; w++) gdImageSetPixel (im, x, w, color); } else { - wstart = x - wid / 2; - for (w = wstart; w < wstart + wid; w++) + int wstart = x - wid / 2; + for (int w = wstart; w < wstart + wid; w++) gdImageSetPixel (im, w, y, color); } } @@ -2882,7 +2882,7 @@ BGD_DECLARE(gdImagePtr) gdImageClone (gdImagePtr src) { dst->paletteQuantizationMethod = src->paletteQuantizationMethod; dst->paletteQuantizationSpeed = src->paletteQuantizationSpeed; dst->paletteQuantizationMinQuality = src->paletteQuantizationMinQuality; - dst->paletteQuantizationMinQuality = src->paletteQuantizationMinQuality; + dst->paletteQuantizationMaxQuality = src->paletteQuantizationMaxQuality; dst->interpolation_id = src->interpolation_id; dst->interpolation = src->interpolation; @@ -2936,10 +2936,6 @@ BGD_DECLARE(gdImagePtr) gdImageClone (gdImagePtr src) { BGD_DECLARE(void) gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h) { - int c; - int x, y; - int tox, toy; - int i; int colorMap[gdMaxColors]; if (dst->trueColor) { @@ -2949,8 +2945,8 @@ BGD_DECLARE(void) gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dst */ if (src->trueColor) { - for (y = 0; (y < h); y++) { - for (x = 0; (x < w); x++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { int c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y); if (c != src->transparent) { gdImageSetPixel (dst, dstX + x, dstY + y, c); @@ -2959,8 +2955,8 @@ BGD_DECLARE(void) gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dst } } else { /* source is palette based */ - for (y = 0; (y < h); y++) { - for (x = 0; (x < w); x++) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { int c = gdImageGetPixel (src, srcX + x, srcY + y); if (c != src->transparent) { gdImageSetPixel(dst, dstX + x, dstY + y, gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c])); @@ -2971,16 +2967,15 @@ BGD_DECLARE(void) gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dst return; } - for (i = 0; (i < gdMaxColors); i++) { + for (int i = 0; i < gdMaxColors; i++) { colorMap[i] = (-1); } - toy = dstY; - for (y = srcY; (y < (srcY + h)); y++) { - tox = dstX; - for (x = srcX; (x < (srcX + w)); x++) { - int nc; + int toy = dstY; + for (int y = srcY; y < (srcY + h); y++) { + int tox = dstX; + for (int x = srcX; x < (srcX + w); x++) { int mapTo; - c = gdImageGetPixel (src, x, y); + int c = gdImageGetPixel (src, x, y); /* Added 7/24/95: support transparent copies */ if (gdImageGetTransparent (src) == c) { tox++; @@ -2998,6 +2993,7 @@ BGD_DECLARE(void) gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dst gdTrueColorGetBlue (c), gdTrueColorGetAlpha (c)); } else if (colorMap[c] == (-1)) { + int nc; /* If it's the same image, mapping is trivial */ if (dst == src) { nc = c; @@ -3201,15 +3197,7 @@ BGD_DECLARE(void) gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH) { - int c; - int x, y; - int tox, toy; - int ydest; - int i; int colorMap[gdMaxColors]; - /* Stretch vectors */ - int *stx; - int *sty; /* We only need to use floating point to determine the correct stretch vector for one line's worth. */ if (overflow2(sizeof (int), srcW)) { @@ -3218,33 +3206,33 @@ BGD_DECLARE(void) gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, if (overflow2(sizeof (int), srcH)) { return; } - stx = (int *) gdMalloc (sizeof (int) * srcW); + /* Stretch vectors */ + int *stx = (int *) gdMalloc (sizeof (int) * srcW); if (!stx) { return; } - sty = (int *) gdMalloc (sizeof (int) * srcH); + int *sty = (int *) gdMalloc (sizeof (int) * srcH); if (!sty) { gdFree(stx); return; } /* Fixed by Mao Morimoto 2.0.16 */ - for (i = 0; (i < srcW); i++) { + for (int i = 0; i < srcW; i++) { stx[i] = dstW * (i + 1) / srcW - dstW * i / srcW; } - for (i = 0; (i < srcH); i++) { + for (int i = 0; i < srcH; i++) { sty[i] = dstH * (i + 1) / srcH - dstH * i / srcH; } - for (i = 0; (i < gdMaxColors); i++) { + for (int i = 0; i < gdMaxColors; i++) { colorMap[i] = (-1); } - toy = dstY; - for (y = srcY; (y < (srcY + srcH)); y++) { - for (ydest = 0; (ydest < sty[y - srcY]); ydest++) { - tox = dstX; - for (x = srcX; (x < (srcX + srcW)); x++) { - int nc = 0; + int toy = dstY; + for (int y = srcY; y < (srcY + srcH); y++) { + for (int ydest = 0; ydest < sty[y - srcY]; ydest++) { + int tox = dstX; + for (int x = srcX; x < (srcX + srcW); x++) { int mapTo; if (!stx[x - srcX]) { continue; @@ -3271,7 +3259,7 @@ BGD_DECLARE(void) gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, } } } else { - c = gdImageGetPixel (src, x, y); + int c = gdImageGetPixel (src, x, y); /* Added 7/24/95: support transparent copies */ if (gdImageGetTransparent (src) == c) { tox += stx[x - srcX]; @@ -3292,6 +3280,7 @@ BGD_DECLARE(void) gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, } else { /* Have we established a mapping for this color? */ if (colorMap[c] == (-1)) { + int nc = 0; /* If it's the same image, mapping is trivial */ if (dst == src) { nc = c; @@ -3313,7 +3302,7 @@ BGD_DECLARE(void) gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, mapTo = colorMap[c]; } } - for (i = 0; (i < stx[x - srcX]); i++) { + for (int i = 0; i < stx[x - srcX]; i++) { gdImageSetPixel (dst, tox, toy, mapTo); tox++; } @@ -3460,21 +3449,18 @@ BGD_DECLARE(void) gdImageCopyResampled (gdImagePtr dst, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH) { - int x, y; if (!dst->trueColor) { gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); return; } - for (y = dstY; (y < dstY + dstH); y++) { - for (x = dstX; (x < dstX + dstW); x++) { - float sy1, sy2, sx1, sx2; - float sx, sy; + for (int y = dstY; (y < dstY + dstH); y++) { + for (int x = dstX; (x < dstX + dstW); x++) { float spixels = 0.0f; float red = 0.0f, green = 0.0f, blue = 0.0f, alpha = 0.0f; float alpha_factor, alpha_sum = 0.0f, contrib_sum = 0.0f; - sy1 = ((float)(y - dstY)) * (float)srcH / (float)dstH; - sy2 = ((float)(y + 1 - dstY)) * (float) srcH / (float) dstH; - sy = sy1; + float sy1 = ((float)(y - dstY)) * (float)srcH / (float)dstH; + float sy2 = ((float)(y + 1 - dstY)) * (float) srcH / (float) dstH; + float sy = sy1; do { float yportion; if (floorf(sy) == floorf(sy1)) { @@ -3488,9 +3474,9 @@ BGD_DECLARE(void) gdImageCopyResampled (gdImagePtr dst, } else { yportion = 1.0f; } - sx1 = ((float)(x - dstX)) * (float) srcW / dstW; - sx2 = ((float)(x + 1 - dstX)) * (float) srcW / dstW; - sx = sx1; + float sx1 = ((float)(x - dstX)) * (float) srcW / dstW; + float sx2 = ((float)(x + 1 - dstX)) * (float) srcW / dstW; + float sx = sx1; do { float xportion; float pcontribution; @@ -3647,15 +3633,8 @@ BGD_DECLARE(void) gdImageOpenPolygon (gdImagePtr im, gdPointPtr p, int n, int c) */ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c) { - int i; - int j; - int index; - int y; - int miny, maxy, pmaxy; int x1, y1; int x2, y2; - int ind1, ind2; - int ints; int fill_color; if (n <= 0) { return; @@ -3689,9 +3668,9 @@ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int return; } } - miny = p[0].y; - maxy = p[0].y; - for (i = 1; (i < n); i++) { + int miny = p[0].y; + int maxy = p[0].y; + for (int i = 1; i < n; i++) { if (p[i].y < miny) { miny = p[i].y; } @@ -3702,7 +3681,7 @@ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int /* necessary special case: horizontal line */ if (n > 1 && miny == maxy) { x1 = x2 = p[0].x; - for (i = 1; (i < n); i++) { + for (int i = 1; i < n; i++) { if (p[i].x < x1) { x1 = p[i].x; } else if (p[i].x > x2) { @@ -3712,7 +3691,7 @@ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int gdImageLine(im, x1, miny, x2, miny, c); return; } - pmaxy = maxy; + int pmaxy = maxy; /* 2.0.16: Optimization by Ilia Chipitsine -- don't waste time offscreen */ /* 2.0.26: clipping rectangle is even better */ if (miny < im->cy1) { @@ -3722,9 +3701,10 @@ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int maxy = im->cy2; } /* Fix in 1.3: count a vertex only once */ - for (y = miny; (y <= maxy); y++) { - ints = 0; - for (i = 0; (i < n); i++) { + for (int y = miny; y <= maxy; y++) { + int ints = 0; + for (int i = 0; i < n; i++) { + int ind1, ind2; if (!i) { ind1 = n - 1; ind2 = 0; @@ -3763,16 +3743,16 @@ BGD_DECLARE(void) gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int cases, insertion sort is a good choice. Also a good choice for future implementations that may wish to indirect through a table. */ - for (i = 1; (i < ints); i++) { - index = im->polyInts[i]; - j = i; + for (int i = 1; i < ints; i++) { + int index = im->polyInts[i]; + int j = i; while ((j > 0) && (im->polyInts[j - 1] > index)) { im->polyInts[j] = im->polyInts[j - 1]; j--; } im->polyInts[j] = index; } - for (i = 0; (i < (ints-1)); i += 2) { + for (int i = 0; i < (ints-1); i += 2) { /* 2.0.29: back to gdImageLine to prevent segfaults when performing a pattern fill */ gdImageLine (im, im->polyInts[i], y, im->polyInts[i + 1], y, @@ -3844,12 +3824,10 @@ BGD_DECLARE(void) gdImageSetThickness (gdImagePtr im, int thickness) */ BGD_DECLARE(void) gdImageSetBrush (gdImagePtr im, gdImagePtr brush) { - int i; im->brush = brush; if ((!im->trueColor) && (!im->brush->trueColor)) { - for (i = 0; (i < gdImageColorsTotal (brush)); i++) { - int index; - index = gdImageColorResolveAlpha (im, + for (int i = 0; (i < gdImageColorsTotal (brush)); i++) { + int index = gdImageColorResolveAlpha (im, gdImageRed (brush, i), gdImageGreen (brush, i), gdImageBlue (brush, i), @@ -3864,10 +3842,9 @@ BGD_DECLARE(void) gdImageSetBrush (gdImagePtr im, gdImagePtr brush) */ BGD_DECLARE(void) gdImageSetTile (gdImagePtr im, gdImagePtr tile) { - int i; im->tile = tile; if ((!im->trueColor) && (!im->tile->trueColor)) { - for (i = 0; (i < gdImageColorsTotal (tile)); i++) { + for (int i = 0; (i < gdImageColorsTotal (tile)); i++) { int index; index = gdImageColorResolveAlpha (im, gdImageRed (tile, i), @@ -4346,7 +4323,6 @@ static void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int co long x, y, inc, frac; long dx, dy,tmp; int w, wid, wstart; - int thick = im->thick; if (!im->trueColor) { /* TBB: don't crash when the image is of the wrong type */ @@ -4373,7 +4349,7 @@ static void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int co * This isn't a problem as computed dy/dx values came from ints above. */ ag = fabs(abs((int)dy) < abs((int)dx) ? cos(atan2(dy, dx)) : sin(atan2(dy, dx))); if (ag != 0) { - wid = (int)(thick / ag); + wid = (int)(im->thick / ag); } else { wid = 1; } diff --git a/deps/libmscgen/gd_lodepng.c b/deps/libmscgen/gd_lodepng.c index b8ed22b96a5..e519ab5169f 100644 --- a/deps/libmscgen/gd_lodepng.c +++ b/deps/libmscgen/gd_lodepng.c @@ -8,13 +8,12 @@ BGD_DECLARE(void) gdImagePng (gdImagePtr im, FILE * outFile) int **ptpixels = im->tpixels; unsigned char *pixelBuffer = (unsigned char *)malloc(3*im->sx*im->sy); unsigned char *pOut = pixelBuffer; - int x,y; - for (y=0;ysy;y++) + for (int y = 0; y < im->sy; y++) { int *pThisRow = *ptpixels++; - for (x=0;xsx;x++) + for (int x = 0; x < im->sx; x++) { - int thisPixel = *pThisRow++; + const int thisPixel = *pThisRow++; *pOut++ = gdTrueColorGetRed(thisPixel); *pOut++ = gdTrueColorGetGreen(thisPixel); *pOut++ = gdTrueColorGetBlue(thisPixel); diff --git a/deps/libmscgen/mscgen_api.c b/deps/libmscgen/mscgen_api.c index e2cdcb9233e..0bd0f61aff2 100644 --- a/deps/libmscgen/mscgen_api.c +++ b/deps/libmscgen/mscgen_api.c @@ -250,7 +250,6 @@ static char *splitStringToWidth(Context *ctx, char *l, unsigned int width) { char *p = l + strlen(l); char *orig = NULL; - int m, n; if (ctx->drw.textWidth(&ctx->drw, l) > width) { @@ -299,8 +298,8 @@ static char *splitStringToWidth(Context *ctx, char *l, unsigned int width) } /* Copy the remaining line to the start of the string */ - m = 0; - n = (int)(p - l); + int m = 0; + int n = (int)(p - l); while (isspace(orig[n]) && orig[n] != '\0') { @@ -464,7 +463,6 @@ static RowInfo *computeCanvasSize(Context *ctx, const MscArcType arcType = MscGetCurrentArcType(m); const int arcGradient = isBoxArc(arcType) ? 0 : getArcGradient(ctx, m, NULL, 0); char **arcLabelLines = NULL; - unsigned int arcLabelLineCount = 0; int startCol = -1, endCol = -1; if (arcType == MSC_ARC_PARALLEL) @@ -492,7 +490,7 @@ static RowInfo *computeCanvasSize(Context *ctx, } /* Work out how the label fits the gap between entities */ - arcLabelLineCount = computeLabelLines(ctx, m, arcType, &arcLabelLines, + unsigned int arcLabelLineCount = computeLabelLines(ctx, m, arcType, &arcLabelLines, MscGetCurrentArcAttrib(m, MSC_ATTR_LABEL), startCol, endCol); @@ -910,12 +908,11 @@ static void arcBox(Context *ctx, const char *lineColour, const char *bgColour) { - unsigned int t; /* Ensure the start is less than or equal to the end */ if(boxStart > boxEnd) { - t = boxEnd; + unsigned int t = boxEnd; boxEnd = boxStart; boxStart = t; } @@ -1677,7 +1674,6 @@ int mscgen_generate(const char *inputFile, const int arcHasArrows = MscGetCurrentArcAttrib(m, MSC_ATTR_NO_ARROWS) == NULL; const int arcHasBiArrows = MscGetCurrentArcAttrib(m, MSC_ATTR_BI_ARROWS) != NULL; char **arcLabelLines = NULL; - unsigned int arcLabelLineCount = 0; int startCol = -1, endCol = -1; if (arcType == MSC_ARC_PARALLEL) @@ -1737,7 +1733,7 @@ int mscgen_generate(const char *inputFile, } /* Work out how the label fits the gap between entities */ - arcLabelLineCount = computeLabelLines(&ctx, m, arcType, &arcLabelLines, + unsigned int arcLabelLineCount = computeLabelLines(&ctx, m, arcType, &arcLabelLines, MscGetCurrentArcAttrib(m, MSC_ATTR_LABEL), startCol, endCol); diff --git a/deps/libmscgen/mscgen_config.h b/deps/libmscgen/mscgen_config.h index 194b96538f5..070643a4ac4 100644 --- a/deps/libmscgen/mscgen_config.h +++ b/deps/libmscgen/mscgen_config.h @@ -2,8 +2,6 @@ #define MSCGEN_CONFIG_H #if defined(_WIN32) && !defined(__CYGWIN__) -#define M_PI 3.14159265358979323846264338327950288 -#define strcasecmp _stricmp #define strdup _strdup #define fileno _fileno #define YY_NO_UNISTD_H 1 @@ -11,6 +9,15 @@ #define HAVE_UNISTD_H 1 #endif +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif +#ifdef _MSC_VER +# ifndef strcasecmp +# define strcasecmp _stricmp +# endif +#endif + /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 diff --git a/deps/libmscgen/mscgen_gd_out.c b/deps/libmscgen/mscgen_gd_out.c index 72c79f5373e..97c58f587cb 100644 --- a/deps/libmscgen/mscgen_gd_out.c +++ b/deps/libmscgen/mscgen_gd_out.c @@ -287,7 +287,7 @@ void gdoTextR(struct ADrawTag *ctx, unsigned int y, const char *string) { - GdoContext *context = getGdoCtx(ctx); + const GdoContext *context = getGdoCtx(ctx); #ifdef USE_FREETYPE int rect[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; const char *r; diff --git a/deps/libmscgen/mscgen_msc.c b/deps/libmscgen/mscgen_msc.c index a18e2619bea..1137406bdc4 100644 --- a/deps/libmscgen/mscgen_msc.c +++ b/deps/libmscgen/mscgen_msc.c @@ -650,11 +650,8 @@ const char *MscGetCurrentEntAttrib(struct MscTag *m, MscAttribType a) const char *MscGetEntAttrib(Msc m, unsigned int entIdx, MscAttribType a) { - struct MscEntityTag *entity; - const char *r; - /* Find the entity */ - entity = m->entityList->head; + struct MscEntityTag *entity = m->entityList->head; while(entIdx > 0 && entity != NULL) { entity = entity->next; @@ -664,7 +661,7 @@ const char *MscGetEntAttrib(Msc m, unsigned int entIdx, MscAttribType a) /* Search the attribute list if the entity was found */ if(entity) { - r = findAttrib(entity->attr, a); + const char *r = findAttrib(entity->attr, a); /* If the entity label was sought but not found, return entity name */ if(r == NULL && a == MSC_ATTR_LABEL) @@ -746,7 +743,7 @@ const char *MscGetCurrentArcAttrib(struct MscTag *m, MscAttribType a) Boolean MscGetOptAsFloat(struct MscTag *m, MscOptType type, float *const f) { - struct MscOptTag *opt = MscFindOpt(m->optList, type); + const struct MscOptTag *opt = MscFindOpt(m->optList, type); if(opt != NULL) { diff --git a/deps/libmscgen/mscgen_ps_out.c b/deps/libmscgen/mscgen_ps_out.c index 32f9eae48ef..4a8aab1d3ff 100644 --- a/deps/libmscgen/mscgen_ps_out.c +++ b/deps/libmscgen/mscgen_ps_out.c @@ -268,7 +268,7 @@ void PsTextR(struct ADrawTag *ctx, unsigned int y, const char *string) { - PsContext *context = getPsCtx(ctx); + const PsContext *context = getPsCtx(ctx); /* Push the string and get its width */ fprintf(getPsFile(ctx), "("); @@ -300,7 +300,7 @@ void PsTextL(struct ADrawTag *ctx, unsigned int y, const char *string) { - PsContext *context = getPsCtx(ctx); + const PsContext *context = getPsCtx(ctx); /* Draw the background box */ setColour(ctx, context->penBgColour); @@ -327,7 +327,7 @@ void PsTextC(struct ADrawTag *ctx, unsigned int y, const char *string) { - PsContext *context = getPsCtx(ctx); + const PsContext *context = getPsCtx(ctx); /* Push the string and get its width */ fprintf(getPsFile(ctx), "("); diff --git a/deps/libmscgen/mscgen_svg_out.c b/deps/libmscgen/mscgen_svg_out.c index 73378be21c5..495649e1cc1 100644 --- a/deps/libmscgen/mscgen_svg_out.c +++ b/deps/libmscgen/mscgen_svg_out.c @@ -301,7 +301,7 @@ void SvgTextR(struct ADrawTag *ctx, unsigned int y, const char *string) { - SvgContext *context = getSvgCtx(ctx); + const SvgContext *context = getSvgCtx(ctx); svgRect(ctx, getSvgBgPen(ctx), x - 2, y - SvgTextHeight(ctx) + 1, x + SvgTextWidth(ctx, string), y - 1); @@ -320,7 +320,7 @@ void SvgTextL(struct ADrawTag *ctx, unsigned int y, const char *string) { - SvgContext *context = getSvgCtx(ctx); + const SvgContext *context = getSvgCtx(ctx); svgRect(ctx, getSvgBgPen(ctx), x - (SvgTextWidth(ctx, string) + 2), y - SvgTextHeight(ctx) + 1, x, y - 1); @@ -341,7 +341,7 @@ void SvgTextC(struct ADrawTag *ctx, unsigned int y, const char *string) { - SvgContext *context = getSvgCtx(ctx); + const SvgContext *context = getSvgCtx(ctx); unsigned int hw = SvgTextWidth(ctx, string) / 2; svgRect(ctx, getSvgBgPen(ctx), x - (hw + 2), y - SvgTextHeight(ctx) + 1, x + hw, y - 1); diff --git a/deps/spdlog/CMakeLists.txt b/deps/spdlog/CMakeLists.txt index 7a554ef47a4..3368e4ddf24 100644 --- a/deps/spdlog/CMakeLists.txt +++ b/deps/spdlog/CMakeLists.txt @@ -33,11 +33,6 @@ elseif(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() -# make sure __cplusplus is defined when using msvc and enable parallel build -if(MSVC) - string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP") -endif() - set(CMAKE_CXX_EXTENSIONS OFF) if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW") @@ -80,6 +75,10 @@ option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/ # sanitizer options option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF) +option(SPDLOG_SANITIZE_THREAD "Enable thread sanitizer in tests" OFF) +if(SPDLOG_SANITIZE_ADDRESS AND SPDLOG_SANITIZE_THREAD) + message(FATAL_ERROR "SPDLOG_SANITIZE_ADDRESS and SPDLOG_SANITIZE_THREAD are mutually exclusive") +endif() # warning options option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF) @@ -115,6 +114,10 @@ else() set(SPDLOG_WCHAR_CONSOLE OFF CACHE BOOL "non supported option" FORCE) endif() +if(MSVC) + option(SPDLOG_MSVC_UTF8 "Enable/disable msvc /utf-8 flag required by fmt lib" ON) +endif() + if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF) else() @@ -129,6 +132,7 @@ option( "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" OFF) option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF) +option(SPDLOG_FWRITE_UNLOCKED "Use the unlocked variant of fwrite. Leave this on unless your libc doesn't have it" ON) # clang-tidy option(SPDLOG_TIDY "run clang-tidy" OFF) @@ -161,7 +165,7 @@ if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS) endif() add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB) - if(MSVC) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(spdlog PUBLIC $<$,$>>:/wd4251 /wd4275>) endif() @@ -194,6 +198,13 @@ if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH) target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h) endif() +# sanitizer support +if(SPDLOG_SANITIZE_ADDRESS) + spdlog_enable_addr_sanitizer(spdlog) +elseif(SPDLOG_SANITIZE_THREAD) + spdlog_enable_thread_sanitizer(spdlog) +endif() + # --------------------------------------------------------------------------------------- # Header only version # --------------------------------------------------------------------------------------- @@ -215,7 +226,7 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL) target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL) - # use external fmt-header-nly + # use external fmt-header-only 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) @@ -227,6 +238,22 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config endif() +# --------------------------------------------------------------------------------------- +# Check if fwrite_unlocked/_fwrite_nolock is available +# --------------------------------------------------------------------------------------- +if(SPDLOG_FWRITE_UNLOCKED) + include(CheckSymbolExists) + if(WIN32) + check_symbol_exists(_fwrite_nolock "stdio.h" HAVE_FWRITE_UNLOCKED) + else() + check_symbol_exists(fwrite_unlocked "stdio.h" HAVE_FWRITE_UNLOCKED) + endif() + if(HAVE_FWRITE_UNLOCKED) + target_compile_definitions(spdlog PRIVATE SPDLOG_FWRITE_UNLOCKED) + target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FWRITE_UNLOCKED) + endif() +endif() + # --------------------------------------------------------------------------------------- # Add required libraries for Android CMake build # --------------------------------------------------------------------------------------- @@ -259,17 +286,31 @@ foreach( endif() endforeach() +if(MSVC) + target_compile_options(spdlog PRIVATE "/Zc:__cplusplus") + target_compile_options(spdlog_header_only INTERFACE "/Zc:__cplusplus") + if(SPDLOG_MSVC_UTF8) + # fmtlib requires the /utf-8 flag when building with msvc. see https://github.com/fmtlib/fmt/pull/4159 on the + # purpose of the additional + # "$<$,$>" + target_compile_options(spdlog PUBLIC $<$,$>:/utf-8>) + target_compile_options(spdlog_header_only + INTERFACE $<$,$>:/utf-8>) + endif() +endif() + # --------------------------------------------------------------------------------------- # If exceptions are disabled, disable them in the bundled fmt as well # --------------------------------------------------------------------------------------- if(SPDLOG_NO_EXCEPTIONS) if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) - target_compile_definitions(spdlog PUBLIC FMT_EXCEPTIONS=0) + target_compile_definitions(spdlog PUBLIC FMT_USE_EXCEPTIONS=0) endif() if(NOT MSVC) target_compile_options(spdlog PRIVATE -fno-exceptions) else() target_compile_options(spdlog PRIVATE /EHs-c-) + target_compile_definitions(spdlog PRIVATE _HAS_EXCEPTIONS=0) endif() endif() # --------------------------------------------------------------------------------------- diff --git a/deps/spdlog/README.md b/deps/spdlog/README.md index c741376eba8..a12bb2bf9eb 100644 --- a/deps/spdlog/README.md +++ b/deps/spdlog/README.md @@ -1,18 +1,25 @@ # spdlog -Very fast, header-only/compiled, C++ logging library. [![ci](https://github.com/gabime/spdlog/actions/workflows/ci.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/ci.yml)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true&branch=v1.x)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) + +[![ci](https://github.com/gabime/spdlog/actions/workflows/linux.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/linux.yml)  +[![ci](https://github.com/gabime/spdlog/actions/workflows/windows.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/windows.yml)  +[![ci](https://github.com/gabime/spdlog/actions/workflows/macos.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/macos.yml)  +[![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true&branch=v1.x)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) + +Fast C++ logging library + ## Install #### Header-only version -Copy the include [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. +Copy the include [folder](include/spdlog) to your build tree and use a C++11 compiler. #### Compiled version (recommended - much faster compile times) ```console $ git clone https://github.com/gabime/spdlog.git $ cd spdlog && mkdir build && cd build -$ cmake .. && make -j +$ cmake .. && cmake --build . ``` -see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use. +see example [CMakeLists.txt](example/CMakeLists.txt) on how to use. ## Platforms * Linux, FreeBSD, OpenBSD, Solaris, AIX @@ -29,8 +36,9 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/ * Gentoo: `emerge dev-libs/spdlog` * Arch Linux: `pacman -S spdlog` * openSUSE: `sudo zypper in spdlog-devel` +* ALT Linux: `apt-get install libspdlog-devel` * vcpkg: `vcpkg install spdlog` -* conan: `spdlog/[>=1.4.1]` +* conan: `conan install --requires=spdlog/[*]` * conda: `conda install -c conda-forge spdlog` * build2: ```depends: spdlog ^1.8.2``` @@ -40,7 +48,7 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/ * Headers only or compiled * Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Asynchronous mode (optional) -* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. +* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting. * Multi/Single threaded loggers. * Various log targets: * Rotating log files. @@ -50,7 +58,7 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/ * Windows event log. * Windows debugger (```OutputDebugString(..)```). * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). - * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. + * Easily [extendable](https://github.com/gabime/spdlog/wiki/Sinks#implementing-your-own-sink) with custom log targets. * Log filtering - log levels can be modified at runtime as well as compile time. * Support for loading log levels from argv or environment var. * [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display them later on demand. @@ -296,7 +304,7 @@ struct fmt::formatter : fmt::formatter { auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) { - return format_to(ctx.out(), "[my_type i={}]", my.i); + return fmt::format_to(ctx.out(), "[my_type i={}]", my.i); } }; @@ -380,6 +388,9 @@ void android_example() int main (int argc, char *argv[]) { spdlog::cfg::load_env_levels(); + // or specify the env variable name: + // MYAPP_LEVEL=info,mylogger=trace && ./example + // spdlog::cfg::load_env_levels("MYAPP_LEVEL"); // or from the command line: // ./example SPDLOG_LEVEL=info,mylogger=trace // #include "spdlog/cfg/argv.h" // for loading levels from argv @@ -456,7 +467,7 @@ void mdc_example() --- ## 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 +Below are some [benchmarks](bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz #### Synchronous mode ``` @@ -508,7 +519,8 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben ``` ## Documentation -Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. + +Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki) pages. --- diff --git a/deps/spdlog/cmake/pch.h.in b/deps/spdlog/cmake/pch.h.in index a5f9415071c..0592ee9def1 100644 --- a/deps/spdlog/cmake/pch.h.in +++ b/deps/spdlog/cmake/pch.h.in @@ -10,7 +10,6 @@ // details/file_helper-inl.h // details/os-inl.h -// fmt/bundled/core.h // fmt/bundled/posix.h // logger-inl.h // sinks/daily_file_sink.h @@ -23,7 +22,6 @@ // details/os-inl.h // details/pattern_formatter-inl.h -// fmt/bundled/core.h // fmt/bundled/format-inl.h #include @@ -73,7 +71,6 @@ // details/registry.h // details/tcp_client-windows.h // details/tcp_client.h -// fmt/bundled/core.h // sinks/android_sink.h // sinks/ansicolor_sink.h // sinks/basic_file_sink.h @@ -150,7 +147,6 @@ // common.h // details/fmt_helper.h -// fmt/bundled/core.h // fmt/bundled/ranges.h #include @@ -255,4 +251,4 @@ #include // spdlog -#include \ No newline at end of file +#include diff --git a/deps/spdlog/cmake/utils.cmake b/deps/spdlog/cmake/utils.cmake index 85fcd80f720..8926657c9a5 100644 --- a/deps/spdlog/cmake/utils.cmake +++ b/deps/spdlog/cmake/utils.cmake @@ -49,7 +49,7 @@ function(spdlog_enable_warnings target_name) endfunction() # Enable address sanitizer (gcc/clang only) -function(spdlog_enable_sanitizer target_name) +function(spdlog_enable_addr_sanitizer target_name) if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") message(FATAL_ERROR "Sanitizer supported only for gcc/clang") endif() @@ -58,5 +58,16 @@ function(spdlog_enable_sanitizer target_name) 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) + target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined) +endfunction() + +# Enable thread sanitizer (gcc/clang only) +function(spdlog_enable_thread_sanitizer target_name) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + message(FATAL_ERROR "Sanitizer supported only for gcc/clang") + endif() + message(STATUS "Thread sanitizer enabled") + target_compile_options(${target_name} PRIVATE -fsanitize=thread) + target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) + target_link_libraries(${target_name} PRIVATE -fsanitize=thread) endfunction() diff --git a/deps/spdlog/include/spdlog/async.h b/deps/spdlog/include/spdlog/async.h index e96abd199b4..92fcd9a7da1 100644 --- a/deps/spdlog/include/spdlog/async.h +++ b/deps/spdlog/include/spdlog/async.h @@ -89,8 +89,7 @@ inline void init_thread_pool(size_t q_size, } inline void init_thread_pool(size_t q_size, size_t thread_count) { - init_thread_pool( - q_size, thread_count, [] {}, [] {}); + init_thread_pool(q_size, thread_count, [] {}, [] {}); } // get the global thread pool. diff --git a/deps/spdlog/include/spdlog/async_logger-inl.h b/deps/spdlog/include/spdlog/async_logger-inl.h index 499800d8806..a681d97c6d4 100644 --- a/deps/spdlog/include/spdlog/async_logger-inl.h +++ b/deps/spdlog/include/spdlog/async_logger-inl.h @@ -33,7 +33,7 @@ SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name, // send the log message to the thread pool SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){ SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){ - pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); + pool_ptr -> post_log(shared_from_this(), msg, overflow_policy_); } else { throw_spdlog_ex("async log: thread pool doesn't exist anymore"); @@ -43,15 +43,13 @@ SPDLOG_LOGGER_CATCH(msg.source) } // send flush request to the thread pool -SPDLOG_INLINE void spdlog::async_logger::flush_(){SPDLOG_TRY{auto pool_ptr = thread_pool_.lock(); -if (!pool_ptr) { +SPDLOG_INLINE void spdlog::async_logger::flush_(){ + SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){ + pool_ptr -> post_flush(shared_from_this(), overflow_policy_); +} +else { throw_spdlog_ex("async flush: thread pool doesn't exist anymore"); } - -std::future future = pool_ptr->post_flush(shared_from_this(), overflow_policy_); -// Wait for the flush operation to complete. -// This might throw exception if the flush message get dropped because of overflow. -future.get(); } SPDLOG_LOGGER_CATCH(source_loc()) } diff --git a/deps/spdlog/include/spdlog/cfg/env.h b/deps/spdlog/include/spdlog/cfg/env.h index 6e554145445..47bf61c721e 100644 --- a/deps/spdlog/include/spdlog/cfg/env.h +++ b/deps/spdlog/include/spdlog/cfg/env.h @@ -25,8 +25,8 @@ namespace spdlog { namespace cfg { -inline void load_env_levels() { - auto env_val = details::os::getenv("SPDLOG_LEVEL"); +inline void load_env_levels(const char* var = "SPDLOG_LEVEL") { + auto env_val = details::os::getenv(var); if (!env_val.empty()) { helpers::load_levels(env_val); } diff --git a/deps/spdlog/include/spdlog/common.h b/deps/spdlog/include/spdlog/common.h index aca483c2077..ba9a2e75f1d 100644 --- a/deps/spdlog/include/spdlog/common.h +++ b/deps/spdlog/include/spdlog/common.h @@ -364,12 +364,7 @@ SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view } #endif -#ifndef SPDLOG_USE_STD_FORMAT -template -inline fmt::basic_string_view to_string_view(fmt::basic_format_string fmt) { - return fmt; -} -#elif __cpp_lib_format >= 202207L +#if defined(SPDLOG_USE_STD_FORMAT) && __cpp_lib_format >= 202207L template SPDLOG_CONSTEXPR_FUNC std::basic_string_view to_string_view( std::basic_format_string fmt) SPDLOG_NOEXCEPT { diff --git a/deps/spdlog/include/spdlog/details/file_helper-inl.h b/deps/spdlog/include/spdlog/details/file_helper-inl.h index 37d1d46fd17..8742b96c7dc 100644 --- a/deps/spdlog/include/spdlog/details/file_helper-inl.h +++ b/deps/spdlog/include/spdlog/details/file_helper-inl.h @@ -101,7 +101,8 @@ SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) { if (fd_ == nullptr) return; size_t msg_size = buf.size(); auto data = buf.data(); - if (std::fwrite(data, 1, msg_size, fd_) != msg_size) { + + if (!details::os::fwrite_bytes(data, msg_size, fd_)) { throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno); } } diff --git a/deps/spdlog/include/spdlog/details/mpmc_blocking_q.h b/deps/spdlog/include/spdlog/details/mpmc_blocking_q.h index 5a474bf33a4..5848cca83f9 100644 --- a/deps/spdlog/include/spdlog/details/mpmc_blocking_q.h +++ b/deps/spdlog/include/spdlog/details/mpmc_blocking_q.h @@ -148,19 +148,19 @@ class mpmc_blocking_queue { #endif size_t overrun_counter() { - std::unique_lock lock(queue_mutex_); + std::lock_guard lock(queue_mutex_); return q_.overrun_counter(); } size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); } size_t size() { - std::unique_lock lock(queue_mutex_); + std::lock_guard lock(queue_mutex_); return q_.size(); } void reset_overrun_counter() { - std::unique_lock lock(queue_mutex_); + std::lock_guard lock(queue_mutex_); q_.reset_overrun_counter(); } diff --git a/deps/spdlog/include/spdlog/details/os-inl.h b/deps/spdlog/include/spdlog/details/os-inl.h index e3c80b9215d..3aaa6b54513 100644 --- a/deps/spdlog/include/spdlog/details/os-inl.h +++ b/deps/spdlog/include/spdlog/details/os-inl.h @@ -265,9 +265,10 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) { return offset; #else - #if defined(sun) || defined(__sun) || defined(_AIX) || \ - (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ - (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) + #if defined(sun) || defined(__sun) || defined(_AIX) || \ + (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ + (!defined(__APPLE__) && !defined(_BSD_SOURCE) && !defined(_GNU_SOURCE) && \ + (!defined(_POSIX_VERSION) || (_POSIX_VERSION < 202405L))) // '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(), @@ -482,13 +483,12 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) { } // find the size to allocate for the result buffer - int result_size = - ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0); + int result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0); if (result_size > 0) { target.resize(result_size); - result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, - target.data(), result_size); + result_size = + ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size); if (result_size > 0) { assert(result_size == target.size()); return; @@ -589,6 +589,18 @@ SPDLOG_INLINE bool fsync(FILE *fp) { #endif } +// Do non-locking fwrite if possible by the os or use the regular locking fwrite +// Return true on success. +SPDLOG_INLINE bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp) { +#if defined(_WIN32) && defined(SPDLOG_FWRITE_UNLOCKED) + return _fwrite_nolock(ptr, 1, n_bytes, fp) == n_bytes; +#elif defined(SPDLOG_FWRITE_UNLOCKED) + return ::fwrite_unlocked(ptr, 1, n_bytes, fp) == n_bytes; +#else + return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes; +#endif +} + } // namespace os } // namespace details } // namespace spdlog diff --git a/deps/spdlog/include/spdlog/details/os.h b/deps/spdlog/include/spdlog/details/os.h index b1069edce03..5fd12bac1ea 100644 --- a/deps/spdlog/include/spdlog/details/os.h +++ b/deps/spdlog/include/spdlog/details/os.h @@ -114,6 +114,10 @@ SPDLOG_API std::string getenv(const char *field); // Return true on success. SPDLOG_API bool fsync(FILE *fp); +// Do non-locking fwrite if possible by the os or use the regular locking fwrite +// Return true on success. +SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp); + } // namespace os } // namespace details } // namespace spdlog diff --git a/deps/spdlog/include/spdlog/details/registry-inl.h b/deps/spdlog/include/spdlog/details/registry-inl.h index f447848ee03..4cbdeb753b3 100644 --- a/deps/spdlog/include/spdlog/details/registry-inl.h +++ b/deps/spdlog/include/spdlog/details/registry-inl.h @@ -54,6 +54,11 @@ SPDLOG_INLINE void registry::register_logger(std::shared_ptr new_logger) register_logger_(std::move(new_logger)); } +SPDLOG_INLINE void registry::register_or_replace(std::shared_ptr new_logger) { + std::lock_guard lock(logger_map_mutex_); + register_or_replace_(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()); @@ -252,10 +257,14 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) { } SPDLOG_INLINE void registry::register_logger_(std::shared_ptr new_logger) { - auto logger_name = new_logger->name(); + auto &logger_name = new_logger->name(); throw_if_exists_(logger_name); loggers_[logger_name] = std::move(new_logger); } +SPDLOG_INLINE void registry::register_or_replace_(std::shared_ptr new_logger) { + loggers_[new_logger->name()] = std::move(new_logger); +} + } // namespace details } // namespace spdlog diff --git a/deps/spdlog/include/spdlog/details/registry.h b/deps/spdlog/include/spdlog/details/registry.h index 8afcbd6f59d..72c70b82bf7 100644 --- a/deps/spdlog/include/spdlog/details/registry.h +++ b/deps/spdlog/include/spdlog/details/registry.h @@ -31,6 +31,7 @@ class SPDLOG_API registry { registry &operator=(const registry &) = delete; void register_logger(std::shared_ptr new_logger); + void register_or_replace(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(); @@ -105,6 +106,7 @@ class SPDLOG_API registry { void throw_if_exists_(const std::string &logger_name); void register_logger_(std::shared_ptr new_logger); + void register_or_replace_(std::shared_ptr new_logger); bool set_level_from_cfg_(logger *logger); std::mutex logger_map_mutex_, flusher_mutex_; std::recursive_mutex tp_mutex_; diff --git a/deps/spdlog/include/spdlog/details/thread_pool-inl.h b/deps/spdlog/include/spdlog/details/thread_pool-inl.h index ccc1dc971e2..68b55369f07 100644 --- a/deps/spdlog/include/spdlog/details/thread_pool-inl.h +++ b/deps/spdlog/include/spdlog/details/thread_pool-inl.h @@ -38,8 +38,7 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, : thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {} SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) - : thread_pool( - q_max_items, threads_n, [] {}, [] {}) {} + : thread_pool(q_max_items, threads_n, [] {}, [] {}) {} // message all threads to terminate gracefully join them SPDLOG_INLINE thread_pool::~thread_pool() { @@ -62,13 +61,9 @@ void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, post_async_msg_(std::move(async_m), overflow_policy); } -std::future SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, - async_overflow_policy overflow_policy) { - std::promise promise; - std::future future = promise.get_future(); - post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush, std::move(promise)), - overflow_policy); - return future; +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(); } @@ -112,7 +107,6 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_() { } case async_msg_type::flush: { incoming_async_msg.worker_ptr->backend_flush_(); - incoming_async_msg.flush_promise.set_value(); return true; } diff --git a/deps/spdlog/include/spdlog/details/thread_pool.h b/deps/spdlog/include/spdlog/details/thread_pool.h index 0d56a091ccb..f22b0782103 100644 --- a/deps/spdlog/include/spdlog/details/thread_pool.h +++ b/deps/spdlog/include/spdlog/details/thread_pool.h @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -28,7 +27,6 @@ enum class async_msg_type { log, flush, terminate }; struct async_msg : log_msg_buffer { async_msg_type msg_type{async_msg_type::log}; async_logger_ptr worker_ptr; - std::promise flush_promise; async_msg() = default; ~async_msg() = default; @@ -58,20 +56,12 @@ struct async_msg : log_msg_buffer { 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)}, - flush_promise{} {} + 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)}, - flush_promise{} {} - - async_msg(async_logger_ptr &&worker, async_msg_type the_type, std::promise &&promise) - : log_msg_buffer{}, - msg_type{the_type}, - worker_ptr{std::move(worker)}, - flush_promise{std::move(promise)} {} + worker_ptr{std::move(worker)} {} explicit async_msg(async_msg_type the_type) : async_msg{nullptr, the_type} {} @@ -98,8 +88,7 @@ class SPDLOG_API thread_pool { void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy); - std::future post_flush(async_logger_ptr &&worker_ptr, - async_overflow_policy overflow_policy); + void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); size_t overrun_counter(); void reset_overrun_counter(); size_t discard_counter(); diff --git a/deps/spdlog/include/spdlog/fmt/bin_to_hex.h b/deps/spdlog/include/spdlog/fmt/bin_to_hex.h index c2998d57ae6..6ed68e427fa 100644 --- a/deps/spdlog/include/spdlog/fmt/bin_to_hex.h +++ b/deps/spdlog/include/spdlog/fmt/bin_to_hex.h @@ -102,7 +102,7 @@ namespace template struct formatter, char> { - const char delimiter = ' '; + char delimiter = ' '; bool put_newlines = true; bool put_delimiters = true; bool use_uppercase = false; @@ -142,8 +142,8 @@ struct formatter, char> { // format the given bytes range as hex template - auto format(const spdlog::details::dump_info &the_range, FormatContext &ctx) const - -> decltype(ctx.out()) { + auto format(const spdlog::details::dump_info &the_range, + FormatContext &ctx) const -> 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; diff --git a/deps/spdlog/include/spdlog/fmt/bundled/args.h b/deps/spdlog/include/spdlog/fmt/bundled/args.h index ad1654bbb60..3ff47880748 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/args.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/args.h @@ -8,14 +8,15 @@ #ifndef FMT_ARGS_H_ #define FMT_ARGS_H_ -#include // std::reference_wrapper -#include // std::unique_ptr -#include +#ifndef FMT_MODULE +# include // std::reference_wrapper +# include // std::unique_ptr +# include +#endif -#include "core.h" +#include "format.h" // std_string_view FMT_BEGIN_NAMESPACE - namespace detail { template struct is_reference_wrapper : std::false_type {}; @@ -28,15 +29,18 @@ auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(v); } -class dynamic_arg_list { - // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for - // templates it doesn't complain about inability to deduce single translation - // unit for placing vtable. So storage_node_base is made a fake template. - template struct node { - virtual ~node() = default; - std::unique_ptr> next; - }; +// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC +// 2022 (v17.10.0). +// +// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for +// templates it doesn't complain about inability to deduce single translation +// unit for placing vtable. So node is made a fake template. +template struct node { + virtual ~node() = default; + std::unique_ptr> next; +}; +class dynamic_arg_list { template struct typed_node : node<> { T value; @@ -62,28 +66,18 @@ class dynamic_arg_list { } // namespace detail /** - \rst - A dynamic version of `fmt::format_arg_store`. - It's equipped with a storage to potentially temporary objects which lifetimes - could be shorter than the format arguments object. - - It can be implicitly converted into `~fmt::basic_format_args` for passing - into type-erased formatting functions such as `~fmt::vformat`. - \endrst + * A dynamic list of formatting arguments with storage. + * + * It can be implicitly converted into `fmt::basic_format_args` for passing + * into type-erased formatting functions such as `fmt::vformat`. */ -template -class dynamic_format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ +template class dynamic_format_arg_store { private: using char_type = typename Context::char_type; template struct need_copy { static constexpr detail::type mapped_type = - detail::mapped_type_constant::value; + detail::mapped_type_constant::value; enum { value = !(detail::is_reference_wrapper::value || @@ -96,7 +90,7 @@ class dynamic_format_arg_store }; template - using stored_type = conditional_t< + using stored_t = conditional_t< std::is_convertible>::value && !detail::is_reference_wrapper::value, std::basic_string, T>; @@ -111,80 +105,72 @@ class dynamic_format_arg_store friend class basic_format_args; - auto get_types() const -> unsigned long long { - return detail::is_unpacked_bit | data_.size() | - (named_info_.empty() - ? 0ULL - : static_cast(detail::has_named_args_bit)); - } - auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } template void emplace_arg(const T& arg) { - data_.emplace_back(detail::make_arg(arg)); + data_.emplace_back(arg); } template void emplace_arg(const detail::named_arg& arg) { - if (named_info_.empty()) { - constexpr const detail::named_arg_info* zero_ptr{nullptr}; - data_.insert(data_.begin(), {zero_ptr, 0}); - } - data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + if (named_info_.empty()) + data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); + data_.emplace_back(detail::unwrap(arg.value)); auto pop_one = [](std::vector>* data) { data->pop_back(); }; std::unique_ptr>, decltype(pop_one)> guard{&data_, pop_one}; named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); - data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + data_[0] = {named_info_.data(), named_info_.size()}; guard.release(); } public: constexpr dynamic_format_arg_store() = default; + operator basic_format_args() const { + return basic_format_args(data(), static_cast(data_.size()), + !named_info_.empty()); + } + /** - \rst - Adds an argument into the dynamic store for later passing to a formatting - function. - - Note that custom types and string types (but not string views) are copied - into the store dynamically allocating memory if necessary. - - **Example**:: - - fmt::dynamic_format_arg_store store; - store.push_back(42); - store.push_back("abc"); - store.push_back(1.5f); - std::string result = fmt::vformat("{} and {} and {}", store); - \endrst - */ + * Adds an argument into the dynamic store for later passing to a formatting + * function. + * + * Note that custom types and string types (but not string views) are copied + * into the store dynamically allocating memory if necessary. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * store.push_back(42); + * store.push_back("abc"); + * store.push_back(1.5f); + * std::string result = fmt::vformat("{} and {} and {}", store); + */ template void push_back(const T& arg) { if (detail::const_check(need_copy::value)) - emplace_arg(dynamic_args_.push>(arg)); + emplace_arg(dynamic_args_.push>(arg)); else emplace_arg(detail::unwrap(arg)); } /** - \rst - Adds a reference to the argument into the dynamic store for later passing to - a formatting function. - - **Example**:: - - fmt::dynamic_format_arg_store store; - char band[] = "Rolling Stones"; - store.push_back(std::cref(band)); - band[9] = 'c'; // Changing str affects the output. - std::string result = fmt::vformat("{}", store); - // result == "Rolling Scones" - \endrst - */ + * Adds a reference to the argument into the dynamic store for later passing + * to a formatting function. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * char band[] = "Rolling Stones"; + * store.push_back(std::cref(band)); + * band[9] = 'c'; // Changing str affects the output. + * std::string result = fmt::vformat("{}", store); + * // result == "Rolling Scones" + */ template void push_back(std::reference_wrapper arg) { static_assert( need_copy::value, @@ -193,41 +179,40 @@ class dynamic_format_arg_store } /** - Adds named argument into the dynamic store for later passing to a formatting - function. ``std::reference_wrapper`` is supported to avoid copying of the - argument. The name is always copied into the store. - */ + * Adds named argument into the dynamic store for later passing to a + * formatting function. `std::reference_wrapper` is supported to avoid + * copying of the argument. The name is always copied into the store. + */ template void push_back(const detail::named_arg& arg) { const char_type* arg_name = dynamic_args_.push>(arg.name).c_str(); if (detail::const_check(need_copy::value)) { emplace_arg( - fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); } else { emplace_arg(fmt::arg(arg_name, arg.value)); } } - /** Erase all elements from the store */ + /// Erase all elements from the store. void clear() { data_.clear(); named_info_.clear(); - dynamic_args_ = detail::dynamic_arg_list(); + dynamic_args_ = {}; } - /** - \rst - Reserves space to store at least *new_cap* arguments including - *new_cap_named* named arguments. - \endrst - */ + /// Reserves space to store at least `new_cap` arguments including + /// `new_cap_named` named arguments. void reserve(size_t new_cap, size_t new_cap_named) { FMT_ASSERT(new_cap >= new_cap_named, - "Set of arguments includes set of named arguments"); + "set of arguments includes set of named arguments"); data_.reserve(new_cap); named_info_.reserve(new_cap_named); } + + /// Returns the number of elements in the store. + size_t size() const noexcept { return data_.size(); } }; FMT_END_NAMESPACE diff --git a/deps/spdlog/include/spdlog/fmt/bundled/base.h b/deps/spdlog/include/spdlog/fmt/bundled/base.h new file mode 100644 index 00000000000..87b3fd7cb48 --- /dev/null +++ b/deps/spdlog/include/spdlog/fmt/bundled/base.h @@ -0,0 +1,2989 @@ +// Formatting library for C++ - the base API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_BASE_H_ +#define FMT_BASE_H_ + +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // memcmp + +# include // std::enable_if +#endif + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 110200 + +// Detect compiler versions. +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif +#if defined(__ICL) +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif +#if defined(_MSC_VER) +# define FMT_MSC_VERSION _MSC_VER +#else +# define FMT_MSC_VERSION 0 +#endif + +// Detect standard library versions. +#ifdef _GLIBCXX_RELEASE +# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE +#else +# define FMT_GLIBCXX_RELEASE 0 +#endif +#ifdef _LIBCPP_VERSION +# define FMT_LIBCPP_VERSION _LIBCPP_VERSION +#else +# define FMT_LIBCPP_VERSION 0 +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +// Detect __has_*. +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif +#ifdef __has_include +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(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 + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Detect C++14 relaxed constexpr. +#ifdef FMT_USE_CONSTEXPR +// Use the provided definition. +#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L +// GCC only allows constexpr member functions in non-literal types since 7.2: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. +# define FMT_USE_CONSTEXPR 1 +#elif FMT_ICC_VERSION +# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 +#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 +# define FMT_USE_CONSTEXPR 1 +#else +# define FMT_USE_CONSTEXPR 0 +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. +#if !defined(__cpp_lib_is_constant_evaluated) +# define FMT_USE_CONSTEVAL 0 +#elif FMT_CPLUSPLUS < 201709L +# define FMT_USE_CONSTEVAL 0 +#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 +# define FMT_USE_CONSTEVAL 0 +#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 +# define FMT_USE_CONSTEVAL 0 +#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L +# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. +#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929 +# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10. +#elif defined(__cpp_consteval) +# define FMT_USE_CONSTEVAL 1 +#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 +# define FMT_USE_CONSTEVAL 1 +#else +# define FMT_USE_CONSTEVAL 0 +#endif +#if FMT_USE_CONSTEVAL +# define FMT_CONSTEVAL consteval +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEVAL +# define FMT_CONSTEXPR20 +#endif + +// Check if exceptions are disabled. +#ifdef FMT_USE_EXCEPTIONS +// Use the provided definition. +#elif defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_USE_EXCEPTIONS 0 +#elif defined(__clang__) && !defined(__cpp_exceptions) +# define FMT_USE_EXCEPTIONS 0 +#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS +# define FMT_USE_EXCEPTIONS 0 +#else +# define FMT_USE_EXCEPTIONS 1 +#endif +#if FMT_USE_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 FMT_NO_UNIQUE_ADDRESS +// Use the provided definition. +#elif FMT_CPLUSPLUS < 202002L +// Not supported. +#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) +# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). +#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION +# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#endif +#ifndef FMT_NO_UNIQUE_ADDRESS +# define FMT_NO_UNIQUE_ADDRESS +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifdef FMT_NODISCARD +// Use the provided definition. +#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +#else +# define FMT_NODISCARD +#endif + +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +#else +# define FMT_VISIBILITY(value) +#endif + +// Detect pragmas. +#define FMT_PRAGMA_IMPL(x) _Pragma(#x) +#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) +// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 +// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. +# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) +#else +# define FMT_PRAGMA_GCC(x) +#endif +#if FMT_CLANG_VERSION +# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) +#else +# define FMT_PRAGMA_CLANG(x) +#endif +#if FMT_MSC_VERSION +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_WARNING(...) +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_PRAGMA_GCC(push_options) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +FMT_PRAGMA_GCC(optimize("Og")) +# define FMT_GCC_OPTIMIZED +#endif +FMT_PRAGMA_CLANG(diagnostic push) + +#ifdef FMT_ALWAYS_INLINE +// Use the provided definition. +#elif FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE inline +#endif +// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. +#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED) +# define FMT_INLINE FMT_ALWAYS_INLINE +#else +# define FMT_INLINE inline +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v11 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_EXPORT +# define FMT_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT +#endif + +#ifdef _WIN32 +# define FMT_WIN32 1 +#else +# define FMT_WIN32 0 +#endif + +#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 +# if defined(FMT_LIB_EXPORT) +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_OPTIMIZE_SIZE +# define FMT_OPTIMIZE_SIZE 0 +#endif + +// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher +// per-call binary size by passing built-in types through the extension API. +#ifndef FMT_BUILTIN_TYPES +# define FMT_BUILTIN_TYPES 1 +#endif + +#define FMT_APPLY_VARIADIC(expr) \ + using unused = int[]; \ + (void)unused { 0, (expr, 0)... } + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older 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; +template +using make_unsigned_t = typename std::make_unsigned::type; +template +using underlying_t = typename std::underlying_type::type; +template using decay_t = typename std::decay::type; +using nullptr_t = decltype(nullptr); + +#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION +// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. +template struct void_t_impl { + using type = void; +}; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif + +struct monostate { + constexpr 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). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +template constexpr auto min_of(T a, T b) -> T { + return a < b ? a : b; +} +template constexpr auto max_of(T a, T b) -> T { + return a > b ? a : b; +} + +namespace detail { +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr auto is_constant_evaluated(bool default_value = false) noexcept + -> bool { +// Workaround for incompatibility between clang 14 and libstdc++ consteval-based +// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. +#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { + return val; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#if defined(FMT_ASSERT) +// Use the provided definition. +#elif defined(NDEBUG) +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + fmt::detail::ignore_unused((condition), (message)) +#else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +#endif + +#ifdef FMT_USE_INT128 +// Use the provided definition. +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +inline auto map(int128_opt x) -> int128_opt { return x; } +inline auto map(uint128_opt x) -> uint128_opt { return x; } +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +inline auto map(int128_opt) -> monostate { return {}; } +inline auto map(uint128_opt) -> monostate { return {}; } +#endif + +#ifndef FMT_USE_BITINT +# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) +#endif + +#if FMT_USE_BITINT +FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") +template using bitint = _BitInt(N); +template using ubitint = unsigned _BitInt(N); +#else +template struct bitint {}; +template struct ubitint {}; +#endif // FMT_USE_BITINT + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); + return static_cast>(value); +} + +template +using unsigned_char = conditional_t; + +// A heuristic to detect std::string and std::[experimental::]string_view. +// It is mainly used to avoid dependency on <[experimental/]string_view>. +template +struct is_std_string_like : std::false_type {}; +template +struct is_std_string_like().find_first_of( + typename T::value_type(), 0))>> + : std::is_convertible().data()), + const typename T::value_type*> {}; + +// Check if the literal encoding is UTF-8. +enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; +enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; + +#ifndef FMT_UNICODE +# define FMT_UNICODE 1 +#endif + +static_assert(!FMT_UNICODE || use_utf8, + "Unicode support requires compiling with /utf-8"); + +template constexpr const char* narrow(const T*) { return nullptr; } +constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } + +template +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) + -> int { + if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); + for (; n != 0; ++s1, ++s2, --n) { + if (*s1 < *s2) return -1; + if (*s1 > *s2) return 1; + } + return 0; +} + +namespace adl { +using namespace std; + +template +auto invoke_back_inserter() + -> decltype(back_inserter(std::declval())); +} // namespace adl + +template +struct is_back_insert_iterator : std::false_type {}; + +template +struct is_back_insert_iterator< + It, bool_constant()), + It>::value>> : std::true_type {}; + +// Extracts a reference to the container from *insert_iterator. +template +inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> + typename OutputIt::container_type& { + struct accessor : OutputIt { + FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {} + using OutputIt::container; + }; + return *accessor(it).container; +} +} // namespace detail + +// Parsing-related public API and forward declarations. +FMT_BEGIN_EXPORT + +/** + * 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::basic_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 value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + + /// Constructs a string view object from a C string and a size. + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} + + constexpr basic_string_view(nullptr_t) = delete; + + /// Constructs a string view object from a C string. +#if FMT_GCC_VERSION + FMT_ALWAYS_INLINE +#endif + FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { +#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION + if (std::is_same::value && !detail::is_constant_evaluated()) { + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. + return; + } +#endif + size_t len = 0; + while (*s++) ++len; + size_ = len; + } + + /// Constructs a string view from a `std::basic_string` or a + /// `std::basic_string_view` object. + template ::value&& std::is_same< + typename S::value_type, Char>::value)> + FMT_CONSTEXPR basic_string_view(const S& s) noexcept + : data_(s.data()), size_(s.size()) {} + + /// Returns a pointer to the string data. + constexpr auto data() const noexcept -> const Char* { return data_; } + + /// Returns the string size. + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept + -> bool { + return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { + return size_ >= 1 && *data_ == c; + } + FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { + return starts_with(basic_string_view(s)); + } + + FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { + int result = + detail::compare(data_, other.data_, min_of(size_, other.size_)); + if (result != 0) return result; + return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + } + + FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, + basic_string_view rhs) -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +// DEPRECATED! Will be merged with is_char and moved to detail. +template struct is_xchar : std::false_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_xchar : std::true_type {}; +#endif + +// Specifies if `T` is a character (code unit) type. +template struct is_char : is_xchar {}; +template <> struct is_char : std::true_type {}; + +template class basic_appender; +using appender = basic_appender; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; + +class context; +template class generic_context; +template class parse_context; + +// Longer aliases for C++20 compatibility. +template using basic_format_parse_context = parse_context; +using format_parse_context = parse_context; +template +using basic_format_context = + conditional_t::value, context, + generic_context>; +using format_context = context; + +template +using buffered_context = + conditional_t::value, context, + generic_context, Char>>; + +template class basic_format_arg; +template class basic_format_args; + +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +/// Reports a format error at compile time or, via a `format_error` exception, +/// at runtime. +// This function is intentionally not constexpr to give a compile-time error. +FMT_NORETURN FMT_API void report_error(const char* message); + +enum class presentation_type : unsigned char { + // Common specifiers: + none = 0, + debug = 1, // '?' + string = 2, // 's' (string, bool) + + // Integral, bool and character specifiers: + dec = 3, // 'd' + hex, // 'x' or 'X' + oct, // 'o' + bin, // 'b' or 'B' + chr, // 'c' + + // String and pointer specifiers: + pointer = 3, // 'p' + + // Floating-point specifiers: + exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) + fixed, // 'f' or 'F' + general, // 'g' or 'G' + hexfloat // 'a' or 'A' +}; + +enum class align { none, left, right, center, numeric }; +enum class sign { none, minus, plus, space }; +enum class arg_id_kind { none, index, name }; + +// Basic format specifiers for built-in and string types. +class basic_specs { + private: + // Data is arranged as follows: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |type |align| w | p | s |u|#|L| f | unused | + // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ + // + // w - dynamic width info + // p - dynamic precision info + // s - sign + // u - uppercase (e.g. 'X' for 'x') + // # - alternate form ('#') + // L - localized + // f - fill size + // + // Bitfields are not used because of compiler bugs such as gcc bug 61414. + enum : unsigned { + type_mask = 0x00007, + align_mask = 0x00038, + width_mask = 0x000C0, + precision_mask = 0x00300, + sign_mask = 0x00C00, + uppercase_mask = 0x01000, + alternate_mask = 0x02000, + localized_mask = 0x04000, + fill_size_mask = 0x38000, + + align_shift = 3, + width_shift = 6, + precision_shift = 8, + sign_shift = 10, + fill_size_shift = 15, + + max_fill_size = 4 + }; + + unsigned data_ = 1 << fill_size_shift; + static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); + + // Character (code unit) type is erased to prevent template bloat. + char fill_data_[max_fill_size] = {' '}; + + FMT_CONSTEXPR void set_fill_size(size_t size) { + data_ = (data_ & ~fill_size_mask) | + (static_cast(size) << fill_size_shift); + } + + public: + constexpr auto type() const -> presentation_type { + return static_cast(data_ & type_mask); + } + FMT_CONSTEXPR void set_type(presentation_type t) { + data_ = (data_ & ~type_mask) | static_cast(t); + } + + constexpr auto align() const -> align { + return static_cast((data_ & align_mask) >> align_shift); + } + FMT_CONSTEXPR void set_align(fmt::align a) { + data_ = (data_ & ~align_mask) | (static_cast(a) << align_shift); + } + + constexpr auto dynamic_width() const -> arg_id_kind { + return static_cast((data_ & width_mask) >> width_shift); + } + FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { + data_ = (data_ & ~width_mask) | (static_cast(w) << width_shift); + } + + FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { + return static_cast((data_ & precision_mask) >> + precision_shift); + } + FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { + data_ = (data_ & ~precision_mask) | + (static_cast(p) << precision_shift); + } + + constexpr bool dynamic() const { + return (data_ & (width_mask | precision_mask)) != 0; + } + + constexpr auto sign() const -> sign { + return static_cast((data_ & sign_mask) >> sign_shift); + } + FMT_CONSTEXPR void set_sign(fmt::sign s) { + data_ = (data_ & ~sign_mask) | (static_cast(s) << sign_shift); + } + + constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } + FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } + + constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } + FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } + FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } + + constexpr auto localized() const -> bool { + return (data_ & localized_mask) != 0; + } + FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } + + constexpr auto fill_size() const -> size_t { + return (data_ & fill_size_mask) >> fill_size_shift; + } + + template ::value)> + constexpr auto fill() const -> const Char* { + return fill_data_; + } + template ::value)> + constexpr auto fill() const -> const Char* { + return nullptr; + } + + template constexpr auto fill_unit() const -> Char { + using uchar = unsigned char; + return static_cast(static_cast(fill_data_[0]) | + (static_cast(fill_data_[1]) << 8) | + (static_cast(fill_data_[2]) << 16)); + } + + FMT_CONSTEXPR void set_fill(char c) { + fill_data_[0] = c; + set_fill_size(1); + } + + template + FMT_CONSTEXPR void set_fill(basic_string_view s) { + auto size = s.size(); + set_fill_size(size); + if (size == 1) { + unsigned uchar = static_cast>(s[0]); + fill_data_[0] = static_cast(uchar); + fill_data_[1] = static_cast(uchar >> 8); + fill_data_[2] = static_cast(uchar >> 16); + return; + } + FMT_ASSERT(size <= max_fill_size, "invalid fill"); + for (size_t i = 0; i < size; ++i) + fill_data_[i & 3] = static_cast(s[i]); + } + + FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { + set_fill_size(specs.fill_size()); + for (size_t i = 0; i < max_fill_size; ++i) + fill_data_[i] = specs.fill_data_[i]; + } +}; + +// Format specifiers for built-in and string types. +struct format_specs : basic_specs { + int width; + int precision; + + constexpr format_specs() : width(0), precision(-1) {} +}; + +/** + * Parsing context consisting of a format string range being parsed and an + * argument counter for automatic indexing. + */ +template class parse_context { + private: + basic_string_view fmt_; + int next_arg_id_; + + enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; + + FMT_CONSTEXPR void do_check_arg_id(int arg_id); + + public: + using char_type = Char; + using iterator = const Char*; + + constexpr explicit parse_context(basic_string_view fmt, + int next_arg_id = 0) + : fmt_(fmt), next_arg_id_(next_arg_id) {} + + /// Returns an iterator to the beginning of the format string range being + /// parsed. + constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } + + /// Returns an iterator past the end of the format string range being parsed. + constexpr auto end() const noexcept -> iterator { return fmt_.end(); } + + /// Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator it) { + fmt_.remove_prefix(detail::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 auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + report_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /// Reports an error if using the automatic argument indexing; otherwise + /// switches to the manual indexing. + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + report_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + FMT_CONSTEXPR void check_arg_id(basic_string_view) { + next_arg_id_ = -1; + } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); +}; + +FMT_END_EXPORT + +namespace detail { + +// Constructs fmt::basic_string_view from types implicitly convertible +// to it, deducing Char. Explicitly convertible types such as the ones returned +// from FMT_STRING are intentionally excluded. +template ::value)> +constexpr auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template ::value)> +constexpr auto to_string_view(const T& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} + +template +struct has_to_string_view : std::false_type {}; +// detail:: is intentional since to_string_view is not an extension point. +template +struct has_to_string_view< + T, void_t()))>> + : std::true_type {}; + +/// String's character (code unit) type. detail:: is intentional to prevent ADL. +template ()))> +using char_t = typename V::value_type; + +enum class type { + none_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(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_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, 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); + +constexpr auto is_integral_type(type t) -> bool { + return t > type::none_type && t <= type::last_integer_type; +} +constexpr auto is_arithmetic_type(type t) -> bool { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +struct view {}; + +template +struct is_view : std::false_type {}; +template +struct is_view> : std::is_base_of {}; + +template struct named_arg; +template struct is_named_arg : std::false_type {}; +template struct is_static_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template struct named_arg : view { + const Char* name; + const T& value; + + named_arg(const Char* n, const T& v) : name(n), value(v) {} + static_assert(!is_named_arg::value, "nested named arguments"); +}; + +template constexpr auto count() -> int { return B ? 1 : 0; } +template constexpr auto count() -> int { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> int { + return count::value...>(); +} +template constexpr auto count_static_named_args() -> int { + return count::value...>(); +} + +template struct named_arg_info { + const Char* name; + int id; +}; + +// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +template +FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, + int named_arg_index, + basic_string_view arg_name) { + for (int i = 0; i < named_arg_index; ++i) { + if (named_args[i].name == arg_name) report_error("duplicate named arg"); + } +} + +template ::value)> +void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { + ++arg_index; +} +template ::value)> +void init_named_arg(named_arg_info* named_args, int& arg_index, + int& named_arg_index, const T& arg) { + check_for_duplicate(named_args, named_arg_index, arg.name); + named_args[named_arg_index++] = {arg.name, arg_index++}; +} + +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, + int&) { + ++arg_index; +} +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, + int& arg_index, int& named_arg_index) { + check_for_duplicate(named_args, named_arg_index, T::name); + named_args[named_arg_index++] = {T::name, arg_index++}; +} + +// 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) && FMT_BUILTIN_TYPES }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +template +using format_as_result = + remove_cvref_t()))>; +template +using format_as_member_result = + remove_cvref_t::format_as(std::declval()))>; + +template +struct use_format_as : std::false_type {}; +// format_as member is only used to avoid injection into the std namespace. +template +struct use_format_as_member : std::false_type {}; + +// Only map owning types because mapping views can be unsafe. +template +struct use_format_as< + T, bool_constant>::value>> + : std::true_type {}; +template +struct use_format_as_member< + T, bool_constant>::value>> + : std::true_type {}; + +template > +using use_formatter = + bool_constant<(std::is_class::value || std::is_enum::value || + std::is_union::value || std::is_array::value) && + !has_to_string_view::value && !is_named_arg::value && + !use_format_as::value && !use_format_as_member::value>; + +template > +auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) + -> decltype(formatter().format(*p, *ctx), std::true_type()); +template auto has_formatter_impl(...) -> std::false_type; + +// T can be const-qualified to check if it is const-formattable. +template constexpr auto has_formatter() -> bool { + return decltype(has_formatter_impl(static_cast(nullptr)))::value; +} + +// Maps formatting argument types to natively supported types or user-defined +// types with formatters. Returns void on errors to be SFINAE-friendly. +template struct type_mapper { + static auto map(signed char) -> int; + static auto map(unsigned char) -> unsigned; + static auto map(short) -> int; + static auto map(unsigned short) -> unsigned; + static auto map(int) -> int; + static auto map(unsigned) -> unsigned; + static auto map(long) -> long_type; + static auto map(unsigned long) -> ulong_type; + static auto map(long long) -> long long; + static auto map(unsigned long long) -> unsigned long long; + static auto map(int128_opt) -> int128_opt; + static auto map(uint128_opt) -> uint128_opt; + static auto map(bool) -> bool; + + template + static auto map(bitint) -> conditional_t; + template + static auto map(ubitint) + -> conditional_t; + + template ::value)> + static auto map(T) -> conditional_t< + std::is_same::value || std::is_same::value, Char, void>; + + static auto map(float) -> float; + static auto map(double) -> double; + static auto map(long double) -> long double; + + static auto map(Char*) -> const Char*; + static auto map(const Char*) -> const Char*; + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + static auto map(const T&) -> conditional_t::value, + basic_string_view, void>; + + static auto map(void*) -> const void*; + static auto map(const void*) -> const void*; + static auto map(volatile void*) -> const void*; + static auto map(const volatile void*) -> const void*; + static auto map(nullptr_t) -> const void*; + template ::value || + std::is_member_pointer::value)> + static auto map(const T&) -> void; + + template ::value)> + static auto map(const T& x) -> decltype(map(format_as(x))); + template ::value)> + static auto map(const T& x) -> decltype(map(formatter::format_as(x))); + + template ::value)> + static auto map(T&) -> conditional_t(), T&, void>; + + template ::value)> + static auto map(const T& named_arg) -> decltype(map(named_arg.value)); +}; + +// detail:: is used to workaround a bug in MSVC 2017. +template +using mapped_t = decltype(detail::type_mapper::map(std::declval())); + +// A type constant after applying type_mapper. +template +using mapped_type_constant = type_constant, Char>; + +template ::value> +using stored_type_constant = std::integral_constant< + type, Context::builtin_types || TYPE == type::int_type ? TYPE + : type::custom_type>; +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public parse_context { + private: + int num_args_; + const type* types_; + using base = parse_context; + + public: + FMT_CONSTEXPR explicit compile_parse_context(basic_string_view fmt, + int num_args, const type* types, + int next_arg_id = 0) + : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) report_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) report_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + ignore_unused(arg_id); + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + report_error("width/precision is not integer"); + } +}; + +// An argument reference. +template union arg_ref { + FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} + FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; +} + +// 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 auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) return static_cast(value); + // Check for overflow. + unsigned max = INT_MAX; + return num_digits == digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align { + switch (c) { + case '<': return align::left; + case '>': return align::right; + case '^': return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = parse_nonnegative_int(begin, end, INT_MAX); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + report_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { + report_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template struct dynamic_spec_handler { + parse_context& ctx; + arg_ref& ref; + arg_id_kind& kind; + + FMT_CONSTEXPR void on_index(int id) { + ref = id; + kind = arg_id_kind::index; + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = id; + kind = arg_id_kind::name; + ctx.check_arg_id(id); + } +}; + +template struct parse_dynamic_spec_result { + const Char* end; + arg_id_kind kind; +}; + +// Parses integer | "{" [arg_id] "}". +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + parse_context& ctx) + -> parse_dynamic_spec_result { + FMT_ASSERT(begin != end, ""); + auto kind = arg_id_kind::none; + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val == -1) report_error("number is too big"); + value = val; + } else { + if (*begin == '{') { + ++begin; + if (begin != end) { + Char c = *begin; + if (c == '}' || c == ':') { + int id = ctx.next_arg_id(); + ref = id; + kind = arg_id_kind::index; + ctx.check_dynamic_spec(id); + } else { + begin = parse_arg_id(begin, end, + dynamic_spec_handler{ctx, ref, kind}); + } + } + if (begin != end && *begin == '}') return {++begin, kind}; + } + report_error("invalid format string"); + } + return {begin, kind}; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + format_specs& specs, arg_ref& width_ref, + parse_context& ctx) -> const Char* { + auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + specs.set_dynamic_width(result.kind); + return result.end; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + format_specs& specs, + arg_ref& precision_ref, + parse_context& ctx) -> const Char* { + ++begin; + if (begin == end) { + report_error("invalid precision"); + return begin; + } + auto result = + parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); + specs.set_dynamic_precision(result.kind); + return result.end; +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, + dynamic_format_specs& specs, + parse_context& ctx, type arg_type) + -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + report_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { + if (!in(arg_type, set)) report_error("invalid format specifier"); + specs.set_type(pres_type); + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.set_align(parse_align(c)); + ++begin; + break; + case '+': + case ' ': + specs.set_sign(c == ' ' ? sign::space : sign::plus); + FMT_FALLTHROUGH; + case '-': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.set_alt(); + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + report_error("format specifier requires numeric argument"); + if (specs.align() == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.set_align(align::numeric); + specs.set_fill('0'); + } + ++begin; + break; + // clang-format off + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '{': + // clang-format on + enter_state(state::width); + begin = parse_width(begin, end, specs, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.set_localized(); + ++begin; + break; + case 'd': return parse_presentation_type(pres::dec, integral_set); + case 'X': specs.set_upper(); FMT_FALLTHROUGH; + case 'x': return parse_presentation_type(pres::hex, integral_set); + case 'o': return parse_presentation_type(pres::oct, integral_set); + case 'B': specs.set_upper(); FMT_FALLTHROUGH; + case 'b': return parse_presentation_type(pres::bin, integral_set); + case 'E': specs.set_upper(); FMT_FALLTHROUGH; + case 'e': return parse_presentation_type(pres::exp, float_set); + case 'F': specs.set_upper(); FMT_FALLTHROUGH; + case 'f': return parse_presentation_type(pres::fixed, float_set); + case 'G': specs.set_upper(); FMT_FALLTHROUGH; + case 'g': return parse_presentation_type(pres::general, float_set); + case 'A': specs.set_upper(); FMT_FALLTHROUGH; + case 'a': return parse_presentation_type(pres::hexfloat, float_set); + case 'c': + if (arg_type == type::bool_type) report_error("invalid format specifier"); + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + report_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + report_error("invalid fill character '{'"); + return begin; + } + auto alignment = parse_align(to_ascii(*fill_end)); + enter_state(state::align, alignment != align::none); + specs.set_fill( + basic_string_view(begin, to_unsigned(fill_end - begin))); + specs.set_align(alignment); + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); + } +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, + const Char* end, + Handler&& handler) + -> const Char* { + ++begin; + if (begin == end) { + handler.on_error("invalid format string"); + return end; + } + int arg_id = 0; + switch (*begin) { + case '}': + handler.on_replacement_field(handler.on_arg_id(), begin); + return begin + 1; + case '{': handler.on_text(begin, begin + 1); return begin + 1; + case ':': arg_id = handler.on_arg_id(); break; + default: { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + } adapter = {handler, 0}; + begin = parse_arg_id(begin, end, adapter); + arg_id = adapter.arg_id; + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(arg_id, begin); + return begin + 1; + } + if (c != ':') { + handler.on_error("missing '}' in format string"); + return end; + } + break; + } + } + begin = handler.on_format_specs(arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + return begin + 1; +} + +template +FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, + Handler&& handler) { + auto begin = fmt.data(), end = begin + fmt.size(); + auto p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); +} + +// Checks char specs and returns true iff the presentation type is char-like. +FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { + auto type = specs.type(); + if (type != presentation_type::none && type != presentation_type::chr && + type != presentation_type::debug) { + return false; + } + if (specs.align() == align::numeric || specs.sign() != sign::none || + specs.alt()) { + report_error("invalid format specifier for char"); + } + return true; +} + +// A base class for compile-time strings. +struct compile_string {}; + +template +FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). +FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { + using mapped_type = remove_cvref_t>; + constexpr bool formattable = + std::is_constructible>::value; + if (!formattable) return ctx.begin(); // Error is reported in the value ctor. + using formatted_type = conditional_t; + return formatter().parse(ctx); +} + +template struct arg_pack {}; + +template +class format_string_checker { + private: + type types_[max_of(1, NUM_ARGS)]; + named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; + compile_parse_context context_; + + using parse_func = auto (*)(parse_context&) -> const Char*; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + + public: + template + FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, + arg_pack) + : types_{mapped_type_constant::value...}, + named_args_{}, + context_(fmt, NUM_ARGS, types_), + parse_funcs_{&invoke_parse...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_static_named_arg(named_args_, arg_index, named_arg_index)); + ignore_unused(arg_index, named_arg_index); + } + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + context_.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + for (int i = 0; i < NUM_NAMED_ARGS; ++i) { + if (named_args_[i].name == id) return named_args_[i].id; + } + if (!DYNAMIC_NAMES) on_error("argument not found"); + return -1; + } + + FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { + on_format_specs(id, begin, begin); // Call parse() on empty specs. + } + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + context_.advance_to(begin); + if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); + + // If id is out of range, it means we do not know the type and cannot parse + // the format at compile time. Instead, skip over content until we finish + // the format spec, accounting for any nested replacements. + for (int bracket_count = 0; + begin != end && (bracket_count > 0 || *begin != '}'); ++begin) { + if (*begin == '{') + ++bracket_count; + else if (*begin == '}') + --bracket_count; + } + return begin; + } + + FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { + report_error(message); + } +}; + +/// A contiguous memory buffer with an optional growing ability. It is an +/// internal class and shouldn't be used directly, only via `memory_buffer`. +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + using grow_fun = void (*)(buffer& buf, size_t capacity); + grow_fun grow_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept + : size_(sz), capacity_(sz), grow_(grow) {} + + constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, + size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /// Sets the buffer data and capacity. + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /// Returns the size of this buffer. + constexpr auto size() const noexcept -> size_t { return size_; } + + /// Returns the capacity of this buffer. + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /// Returns a pointer to the buffer data (not null-terminated). + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /// Clears this buffer. + FMT_CONSTEXPR void clear() { size_ = 0; } + + // Tries resizing the buffer to contain `count` elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR void try_resize(size_t count) { + try_reserve(count); + size_ = min_of(count, capacity_); + } + + // Tries increasing the buffer capacity to `new_capacity`. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow_(*this, new_capacity); + } + + FMT_CONSTEXPR void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /// Appends data to the end of the buffer. + template +// Workaround for MSVC2019 to fix error C2893: Failed to specialize function +// template 'void fmt::v11::detail::buffer::append(const U *,const U *)'. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940 + FMT_CONSTEXPR20 +#endif + void + append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + // A loop is faster than memcpy on small sizes. + T* out = ptr_ + size_; + for (size_t i = 0; i < count; ++i) out[i] = begin[i]; + size_ += count; + begin += count; + } + } + + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + constexpr explicit buffer_traits(size_t) {} + constexpr auto count() const -> size_t { return 0; } + constexpr auto limit(size_t size) const -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + constexpr auto count() const -> size_t { return count_; } + FMT_CONSTEXPR auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return min_of(size, n); + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buffer_size) static_cast(buf).flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + const T* begin = data_; + const T* end = begin + this->limit(size); + while (begin != end) *out_++ = *begin++; + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : Traits(other), + buffer(grow, data_, 0, buffer_size), + out_(other.out_) {} + ~iterator_buffer() { + // Don't crash if flush fails during unwinding. + FMT_TRY { flush(); } + FMT_CATCH(...) {} + } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) + static_cast(buf).flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : fixed_buffer_traits(other), + buffer(static_cast(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer : public buffer { + public: + explicit iterator_buffer(T* out, size_t = 0) + : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +template +class container_buffer : public buffer { + private: + using value_type = typename Container::value_type; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { + auto& self = static_cast(buf); + self.container.resize(capacity); + self.set(&self.container[0], capacity); + } + + public: + Container& container; + + explicit container_buffer(Container& c) + : buffer(grow, c.size()), container(c) {} +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer< + OutputIt, + enable_if_t::value && + is_contiguous::value, + typename OutputIt::container_type::value_type>> + : public container_buffer { + private: + using base = container_buffer; + + public: + explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} + explicit iterator_buffer(OutputIt out, size_t = 0) + : base(get_container(out)) {} + + auto out() -> OutputIt { return OutputIt(this->container); } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() != buffer_size) return; + static_cast(buf).count_ += buf.size(); + buf.clear(); + } + + public: + FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} + + constexpr auto count() const noexcept -> size_t { + return count_ + this->size(); + } +}; + +template +struct is_back_insert_iterator> : std::true_type {}; + +template +struct has_back_insert_iterator_container_append : std::false_type {}; +template +struct has_back_insert_iterator_container_append< + OutputIt, InputIt, + void_t()) + .append(std::declval(), + std::declval()))>> : std::true_type {}; + +// An optimized version of std::copy with the output value type (T). +template ::value&& + has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + get_container(out).append(begin, end); + return out; +} + +template ::value && + !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + auto& c = get_container(out); + c.insert(c.end(), begin, end); + return out; +} + +template ::value)> +FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template +FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { + return copy(s.begin(), s.end(), out); +} + +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + +// Maps an output iterator to a buffer. +template ::value)> +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { + return get_container(out); +} + +template +auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { + return buf.out(); +} +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; +} + +// This type is intentionally undefined, only used for errors. +template struct type_is_unformattable_for; + +template struct string_value { + const Char* data; + size_t size; + auto str() const -> basic_string_view { return {data, size}; } +}; + +template struct custom_value { + using char_type = typename Context::char_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +struct custom_tag {}; + +#if !FMT_BUILTIN_TYPES +# define FMT_BUILTIN , monostate +#else +# define FMT_BUILTIN +#endif + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt 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; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(signed char x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(signed short x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(int x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} + FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} + FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN) + : value(ulong_type(x)) {} + constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} + constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN) + : ulong_long_value(x) {} + FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {} + FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {} + constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} + + template + constexpr FMT_INLINE value(bitint x FMT_BUILTIN) : long_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + template + constexpr FMT_INLINE value(ubitint x FMT_BUILTIN) : ulong_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + + template ::value)> + constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + } + + constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} + constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} + FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} + + FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { + static_assert(std::is_same::value, + "mixing character types is disallowed"); + auto sv = to_string_view(x); + string.data = sv.data(); + string.size = sv.size(); + } + FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(const volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(nullptr_t) : pointer(nullptr) {} + + template ::value || + std::is_member_pointer::value)> + value(const T&) { + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + static_assert(sizeof(T) == 0, + "formatting of non-void pointers is disallowed"); + } + + template ::value)> + value(const T& x) : value(format_as(x)) {} + template ::value)> + value(const T& x) : value(formatter::format_as(x)) {} + + template ::value)> + value(const T& named_arg) : value(named_arg.value) {} + + template ::value || !FMT_BUILTIN_TYPES)> + FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {} + + FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + private: + template ())> + FMT_CONSTEXPR value(T& x, custom_tag) { + using value_type = remove_const_t; + // T may overload operator& e.g. std::vector::reference in libc++. + if (!is_constant_evaluated()) { + custom.value = + const_cast(&reinterpret_cast(x)); + } else { + custom.value = nullptr; +#if defined(__cpp_if_constexpr) + if constexpr (std::is_same*>::value) + custom.value = const_cast(&x); +#endif + } + custom.format = format_custom>; + } + + template ())> + FMT_CONSTEXPR value(const T&, custom_tag) { + // Cannot format an argument; to make type T formattable provide a + // formatter specialization: https://fmt.dev/latest/api.html#udt. + type_is_unformattable_for _; + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom(void* arg, parse_context& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + // format must be const for compatibility with std::format and compilation. + const auto& cf = f; + ctx.advance_to(cf.format(*static_cast(arg), ctx)); + } +}; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +template +struct is_output_iterator : std::false_type {}; + +template <> struct is_output_iterator : std::true_type {}; + +template +struct is_output_iterator< + It, T, + enable_if_t&>()++), + T>::value>> : std::true_type {}; + +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to an std::locale to avoid a heavy include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template locale_ref(const Locale& loc); + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +constexpr auto make_descriptor() -> unsigned long long { + return NUM_ARGS <= max_packed_args ? encode_types() + : is_unpacked_bit | NUM_ARGS; +} + +template +using arg_t = conditional_t, + basic_format_arg>; + +template +struct named_arg_store { + // args_[0].named_args points to named_args to avoid bloating format_args. + arg_t args[1 + NUM_ARGS]; + named_arg_info named_args[NUM_NAMED_ARGS]; + + template + FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) + : args{{named_args, NUM_NAMED_ARGS}, values...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_named_arg(named_args, arg_index, named_arg_index, values)); + } + + named_arg_store(named_arg_store&& rhs) { + args[0] = {named_args, NUM_NAMED_ARGS}; + for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) + args[i] = rhs.args[i]; + for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) + named_args[i] = rhs.named_args[i]; + } + + named_arg_store(const named_arg_store& rhs) = delete; + named_arg_store& operator=(const named_arg_store& rhs) = delete; + named_arg_store& operator=(named_arg_store&& rhs) = delete; + operator const arg_t*() const { return args + 1; } +}; + +// An array of references to arguments. It can be implicitly converted to +// `basic_format_args` for passing into type-erased formatting functions +// such as `vformat`. It is a plain struct to reduce binary size in debug mode. +template +struct format_arg_store { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + using type = + conditional_t[max_of(1, NUM_ARGS)], + named_arg_store>; + type args; +}; + +// TYPE can be different from type_constant, e.g. for __float128. +template struct native_formatter { + private: + dynamic_format_specs specs_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); + if (const_check(TYPE == type::char_type)) check_char_specs(specs_); + return end; + } + + template + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.set_type(set ? presentation_type::debug : presentation_type::none); + } + + FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline") + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template +struct locking + : bool_constant::value == type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + +FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc = {}); + +#if FMT_WIN32 +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +#else // format_args is passed by reference since it is defined later. +inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} +#endif +} // namespace detail + +// The main public API. + +template +FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { + // Argument id is only checked at compile time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && use_constexpr_cast) { + auto ctx = static_cast*>(this); + if (arg_id >= ctx->num_args()) report_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { + using detail::compile_parse_context; + if (detail::is_constant_evaluated() && use_constexpr_cast) + static_cast*>(this)->check_dynamic_spec(arg_id); +} + +FMT_BEGIN_EXPORT + +// An output iterator that appends to a buffer. It is used instead of +// back_insert_iterator to reduce symbol sizes and avoid dependency. +template class basic_appender { + protected: + detail::buffer* container; + + public: + using container_type = detail::buffer; + + FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} + + FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { + container->push_back(c); + return *this; + } + FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } +}; + +// A formatting argument. Context is a template parameter for the compiled API +// where output can be unbuffered. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + friend class basic_format_args; + + using char_type = typename Context::char_type; + + public: + class handle { + private: + detail::custom_value custom_; + + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(parse_context& parse_ctx, Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + template + basic_format_arg(T&& val) + : value_(val), type_(detail::stored_type_constant::value) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + auto type() const -> detail::type { return type_; } + + /** + * 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`. + */ + template + FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { + using detail::map; + switch (type_) { + case detail::type::none_type: break; + case detail::type::int_type: return vis(value_.int_value); + case detail::type::uint_type: return vis(value_.uint_value); + case detail::type::long_long_type: return vis(value_.long_long_value); + case detail::type::ulong_long_type: return vis(value_.ulong_long_value); + case detail::type::int128_type: return vis(map(value_.int128_value)); + case detail::type::uint128_type: return vis(map(value_.uint128_value)); + case detail::type::bool_type: return vis(value_.bool_value); + case detail::type::char_type: return vis(value_.char_value); + case detail::type::float_type: return vis(value_.float_value); + case detail::type::double_type: return vis(value_.double_value); + case detail::type::long_double_type: return vis(value_.long_double_value); + case detail::type::cstring_type: return vis(value_.string.data); + case detail::type::string_type: return vis(value_.string.str()); + case detail::type::pointer_type: return vis(value_.pointer); + case detail::type::custom_type: return vis(handle(value_.custom)); + } + return vis(monostate()); + } + + auto format_custom(const char_type* parse_begin, + parse_context& parse_ctx, Context& ctx) + -> bool { + if (type_ != detail::type::custom_type) return false; + parse_ctx.advance_to(parse_begin); + value_.custom.format(value_.custom.value, parse_ctx, ctx); + return true; + } +}; + +/** + * A view of a collection of formatting arguments. To avoid lifetime issues it + * should only be used as a parameter type in type-erased functions such as + * `vformat`: + * + * void vlog(fmt::string_view fmt, fmt::format_args args); // OK + * fmt::format_args args = fmt::make_format_args(); // Dangling reference + */ +template class basic_format_args { + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since 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 detail::value* values_; + const basic_format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + constexpr auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + template + using store = + detail::format_arg_store; + + public: + using format_arg = basic_format_arg; + + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /// Constructs a `basic_format_args` object from `format_arg_store`. + template + constexpr FMT_ALWAYS_INLINE basic_format_args( + const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + values_(s.args) {} + + template detail::max_packed_args)> + constexpr basic_format_args(const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + args_(s.args) {} + + /// Constructs a `basic_format_args` object from a dynamic list of arguments. + constexpr basic_format_args(const format_arg* args, int count, + bool has_named = false) + : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | + (has_named ? +detail::has_named_args_bit : 0)), + args_(args) {} + + /// Returns the argument with the specified id. + FMT_CONSTEXPR auto get(int id) const -> format_arg { + auto arg = format_arg(); + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (static_cast(id) >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +// A formatting context. +class context { + private: + appender out_; + format_args args_; + FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + + public: + /// The character type for the output. + using char_type = char; + + using iterator = appender; + using format_arg = basic_format_arg; + using parse_context_type FMT_DEPRECATED = parse_context<>; + template using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + /// Constructs a `context` object. References to the arguments are stored + /// in the object so make sure they have appropriate lifetimes. + FMT_CONSTEXPR context(iterator out, format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + context(context&&) = default; + context(const context&) = delete; + void operator=(const context&) = delete; + + FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } + inline auto arg(string_view name) const -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(string_view name) const -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() const -> iterator { return out_; } + + // Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator) {} + + FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } +}; + +template struct runtime_format_string { + basic_string_view str; +}; + +/** + * Creates a runtime format string. + * + * **Example**: + * + * // Check format string at runtime instead of compile-time. + * fmt::print(fmt::runtime("{:d}"), "I am not a number"); + */ +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } + +/// A compile-time format string. Use `format_string` in the public API to +/// prevent type deduction. +template struct fstring { + private: + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + string_view str; + using t = fstring; + + // Reports a compile-time error if S is not a valid format string for T. + template + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { + using namespace detail; + static_assert(count<(is_view>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) { + auto sv = string_view(str); + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(sv, checker(sv, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { + FMT_CONSTEXPR auto sv = string_view(S()); + FMT_CONSTEXPR int unused = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(unused); + } + fstring(runtime_format_string<> fmt) : str(fmt.str) {} + + // Returning by reference generates better code in debug mode. + FMT_ALWAYS_INLINE operator const string_view&() const { return str; } + auto get() const -> string_view { return str; } +}; + +template using format_string = typename fstring::t; + +template +using is_formattable = bool_constant::value, int*, T>, Char>, + void>::value>; +#ifdef __cpp_concepts +template +concept formattable = is_formattable, Char>::value; +#endif + +template +using has_formatter FMT_DEPRECATED = std::is_constructible>; + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> + : detail::native_formatter::value> { +}; + +/** + * Constructs an object that stores references to arguments and can be + * implicitly converted to `format_args`. `Context` can be omitted in which case + * it defaults to `context`. See `arg` for lifetime considerations. + */ +// Take arguments by lvalue references to avoid some lifetime issues, e.g. +// auto args = make_format_args(std::string()); +template (), + unsigned long long DESC = detail::make_descriptor()> +constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) + -> detail::format_arg_store { + // Suppress warnings for pathological types convertible to detail::value. + FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion") + return {{args...}}; +} + +template +using vargs = + detail::format_arg_store(), + detail::make_descriptor()>; + +/** + * Returns a named argument to be used in a formatting function. + * It should only be used in a call to a formatting function. + * + * **Example**: + * + * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + return {name, arg}; +} + +/// Formats a string and writes the output to `out`. +template , + char>::value)> +auto vformat_to(OutputIt&& out, string_view fmt, format_args args) + -> remove_cvref_t { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); +} + +/** + * Formats `args` according to specifications in `fmt`, writes the result to + * the output iterator `out` and returns the iterator past the end of the output + * range. `format_to` does not append a terminating null character. + * + * **Example**: + * + * auto out = std::vector(); + * fmt::format_to(std::back_inserter(out), "{}", 42); + */ +template , + char>::value)> +FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) + -> remove_cvref_t { + return vformat_to(out, fmt.str, vargs{{args...}}); +} + +template struct format_to_n_result { + /// Iterator past the end of the output range. + OutputIt out; + /// Total (not truncated) output size. + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + * Formats `args` according to specifications in `fmt`, writes up to `n` + * characters of the result to the output iterator `out` and returns the total + * (not truncated) output size and the iterator past the end of the output + * range. `format_to_n` does not append a terminating null character. + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt.str, vargs{{args...}}); +} + +struct format_to_result { + /// Pointer to just after the last successful write in the array. + char* out; + /// Specifies if the output was truncated. + bool truncated; + + FMT_CONSTEXPR operator char*() const { + // Report truncation to prevent silent data loss. + if (truncated) report_error("output is truncated"); + return out; + } +}; + +template +auto vformat_to(char (&out)[N], string_view fmt, format_args args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt, args); + return {result.out, result.size > N}; +} + +template +FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); + return {result.out, result.size > N}; +} + +/// Returns the number of chars in the output of `format(fmt, args...)`. +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(FILE* f, string_view fmt, format_args args); +FMT_API void vprintln(FILE* f, string_view fmt, format_args args); +FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::print("The answer is {}.", 42); + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(stdout, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) + : vprint(fmt.str, va); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the + * output to the file `f`. + * + * **Example**: + * + * fmt::print(stderr, "Don't {}!", "panic"); + */ +template +FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(f, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(f, fmt.str, va) + : vprint(f, fmt.str, va); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to the file `f` followed by a newline. +template +FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + return detail::const_check(detail::use_utf8) + ? vprintln(f, fmt.str, va) + : detail::vprint_mojibake(f, fmt.str, va, true); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to `stdout` followed by a newline. +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, static_cast(args)...); +} + +FMT_END_EXPORT +FMT_PRAGMA_CLANG(diagnostic pop) +FMT_PRAGMA_GCC(pop_options) +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_BASE_H_ diff --git a/deps/spdlog/include/spdlog/fmt/bundled/chrono.h b/deps/spdlog/include/spdlog/fmt/bundled/chrono.h index 9d54574e168..e0c81589ea1 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/chrono.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/chrono.h @@ -8,51 +8,21 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#include -#include -#include // std::isfinite -#include // std::memcpy -#include -#include -#include -#include -#include - -#include "ostream.h" // formatbuf - -FMT_BEGIN_NAMESPACE - -// Check if std::chrono::local_t is available. -#ifndef FMT_USE_LOCAL_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_LOCAL_TIME 0 -# endif +#ifndef FMT_MODULE +# include +# include +# include // std::isfinite +# include // std::memcpy +# include +# include +# include +# include +# include #endif -// Check if std::chrono::utc_timestamp is available. -#ifndef FMT_USE_UTC_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_UTC_TIME 0 -# endif -#endif +#include "format.h" -// Enable tzset. -#ifndef FMT_USE_TZSET -// UWP doesn't provide _tzset. -# if FMT_HAS_INCLUDE("winapifamily.h") -# include -# endif -# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ - (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# define FMT_USE_TZSET 1 -# else -# define FMT_USE_TZSET 0 -# endif -#endif +FMT_BEGIN_NAMESPACE // Enable safe chrono durations, unless explicitly disabled. #ifndef FMT_SAFE_DURATION_CAST @@ -94,10 +64,8 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) 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. - */ +/// 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 != @@ -185,61 +153,7 @@ FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { return from; } -/** - * safe duration cast between integral durations - */ -template ::value), - FMT_ENABLE_IF(std::is_integral::value)> -auto safe_duration_cast(std::chrono::duration from, - int& ec) -> To { - 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 (detail::const_check(Factor::num != 1)) { - const auto max1 = detail::max_value() / Factor::num; - if (count > max1) { - ec = 1; - return {}; - } - const auto min1 = - (std::numeric_limits::min)() / Factor::num; - if (detail::const_check(!std::is_unsigned::value) && - count < min1) { - ec = 1; - return {}; - } - count *= Factor::num; - } - - if (detail::const_check(Factor::den != 1)) count /= Factor::den; - auto tocount = lossless_integral_conversion(count, ec); - return ec ? To() : To(tocount); -} - -/** - * safe duration_cast between floating point durations - */ +/// Safe duration_cast between floating point durations template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> @@ -318,17 +232,94 @@ auto safe_duration_cast(std::chrono::duration from, } // namespace safe_duration_cast #endif +namespace detail { + +// Check if std::chrono::utc_time is available. +#ifdef FMT_USE_UTC_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_UTC_TIME 0 +#endif +#if FMT_USE_UTC_TIME +using utc_clock = std::chrono::utc_clock; +#else +struct utc_clock { + template void to_sys(T); +}; +#endif + +// Check if std::chrono::local_time is available. +#ifdef FMT_USE_LOCAL_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_LOCAL_TIME 0 +#endif +#if FMT_USE_LOCAL_TIME +using local_t = std::chrono::local_t; +#else +struct local_t {}; +#endif + +} // namespace detail + +template +using sys_time = std::chrono::time_point; + +template +using utc_time = std::chrono::time_point; + +template +using local_time = std::chrono::time_point; + +namespace detail { + // Prevents expansion of a preceding token as a function-style macro. // Usage: f FMT_NOMACRO() #define FMT_NOMACRO -namespace detail { template struct null {}; inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } inline auto localtime_s(...) -> null<> { return null<>(); } inline auto gmtime_r(...) -> null<> { return null<>(); } inline auto gmtime_s(...) -> null<> { return null<>(); } +// It is defined here and not in ostream.h because the latter has expensive +// includes. +template class formatbuf : public StreamBuf { + private: + using char_type = typename StreamBuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename StreamBuf::int_type; + using traits_type = typename StreamBuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; @@ -341,20 +332,16 @@ template struct codecvt_result { }; template -void write_codecvt(codecvt_result& out, string_view in_buf, +void write_codecvt(codecvt_result& out, string_view in, const std::locale& loc) { -#if FMT_CLANG_VERSION -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated" - auto& f = std::use_facet>(loc); -# pragma clang diagnostic pop -#else + FMT_PRAGMA_CLANG(diagnostic push) + FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") auto& f = std::use_facet>(loc); -#endif + FMT_PRAGMA_CLANG(diagnostic pop) auto mb = std::mbstate_t(); const char* from_next = nullptr; - auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, - std::begin(out.buf), std::end(out.buf), out.end); + auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), + std::end(out.buf), out.end); if (result != std::codecvt_base::ok) FMT_THROW(format_error("failed to format time")); } @@ -362,11 +349,12 @@ void write_codecvt(codecvt_result& out, string_view in_buf, template auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -> OutputIt { - if (detail::is_utf8() && loc != get_classic_locale()) { + if (const_check(detail::use_utf8) && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. -#if FMT_MSC_VERSION != 0 || \ - (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && \ + (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. using code_unit = wchar_t; @@ -382,9 +370,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) to_utf8>(); if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) FMT_THROW(format_error("failed to format time")); - return copy_str(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } - return copy_str(in.data(), in.data() + in.size(), out); + return copy(in.data(), in.data() + in.size(), out); } template OutputIt { codecvt_result unit; write_codecvt(unit, sv, loc); - return copy_str(unit.buf, unit.end, out); + return copy(unit.buf, unit.end, out); } template -struct is_same_arithmetic_type - : public std::integral_constant::value && - std::is_integral::value) || - (std::is_floating_point::value && - std::is_floating_point::value)> { -}; +template +using is_similar_arithmetic_type = + bool_constant<(std::is_integral::value && std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)>; + +FMT_NORETURN inline void throw_duration_error() { + FMT_THROW(format_error("cannot format duration")); +} + +// Cast one integral duration to another with an overflow check. +template ::value&& + std::is_integral::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if !FMT_SAFE_DURATION_CAST + return std::chrono::duration_cast(from); +#else + // The conversion factor: to.count() == factor * from.count(). + using factor = std::ratio_divide; + + using common_rep = typename std::common_type::type; + + int ec = 0; + auto count = safe_duration_cast::lossless_integral_conversion( + from.count(), ec); + if (ec) throw_duration_error(); + + // Multiply from.count() by factor and check for overflow. + if (const_check(factor::num != 1)) { + if (count > max_value() / factor::num) throw_duration_error(); + const auto min = (std::numeric_limits::min)() / factor::num; + if (const_check(!std::is_unsigned::value) && count < min) + throw_duration_error(); + count *= factor::num; + } + if (const_check(factor::den != 1)) count /= factor::den; + auto to = + To(safe_duration_cast::lossless_integral_conversion( + count, ec)); + if (ec) throw_duration_error(); + return to; +#endif +} -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { +template ::value&& + std::is_floating_point::value)> +auto duration_cast(std::chrono::duration from) -> To { #if FMT_SAFE_DURATION_CAST // Throwing version of safe_duration_cast is only available for // integer to integer or float to float casts. int ec; To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); + if (ec) throw_duration_error(); return to; #else // Standard duration cast, may overflow. @@ -458,57 +483,81 @@ auto fmt_duration_cast(std::chrono::duration from) -> To { #endif } -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(!is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { +template ::value)> +auto duration_cast(std::chrono::duration from) -> To { // Mixed integer <-> float cast is not supported by safe_duration_cast. return std::chrono::duration_cast(from); } template -auto to_time_t( - std::chrono::time_point time_point) - -> std::time_t { +auto to_time_t(sys_time time_point) -> std::time_t { // Cannot use std::chrono::system_clock::to_time_t since this would first // require a cast to std::chrono::system_clock::time_point, which could // overflow. - return fmt_duration_cast>( + return detail::duration_cast>( time_point.time_since_epoch()) .count(); } + +namespace tz { + +// DEPRECATED! +struct time_zone { + template + auto to_sys(LocalTime) -> sys_time { + return {}; + } +}; +template auto current_zone(T...) -> time_zone* { + return nullptr; +} + +template void _tzset(T...) {} +} // namespace tz + +// DEPRECATED! +inline void tzset_once() { + static bool init = []() { + using namespace tz; + _tzset(); + return false; + }(); + ignore_unused(init); +} } // namespace detail FMT_BEGIN_EXPORT /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in local time. Unlike ``std::localtime``, this function is - thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in local time. Unlike `std::localtime`, this function is + * thread-safe on most platforms. */ -inline auto localtime(std::time_t time) -> std::tm { +FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; - dispatcher(std::time_t t) : time_(t) {} + inline dispatcher(std::time_t t) : time_(t) {} - auto run() -> bool { + inline auto run() -> bool { using namespace fmt::detail; return handle(localtime_r(&time_, &tm_)); } - auto handle(std::tm* tm) -> bool { return tm != nullptr; } + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - auto handle(detail::null<>) -> bool { + inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(localtime_s(&tm_, &time_)); } - auto fallback(int res) -> bool { return res == 0; } + inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { + inline auto fallback(detail::null<>) -> bool { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); if (tm) tm_ = *tm; @@ -524,40 +573,42 @@ inline auto localtime(std::time_t time) -> std::tm { #if FMT_USE_LOCAL_TIME template -inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime( - detail::to_time_t(std::chrono::current_zone()->to_sys(time))); +FMT_DEPRECATED auto localtime(std::chrono::local_time time) + -> std::tm { + using namespace std::chrono; + using namespace detail::tz; + return localtime(detail::to_time_t(current_zone()->to_sys(time))); } #endif /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this - function is thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this + * function is thread-safe on most platforms. */ inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; - dispatcher(std::time_t t) : time_(t) {} + inline dispatcher(std::time_t t) : time_(t) {} - auto run() -> bool { + inline auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } - auto handle(std::tm* tm) -> bool { return tm != nullptr; } + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - auto handle(detail::null<>) -> bool { + inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } - auto fallback(int res) -> bool { return res == 0; } + inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { + inline auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; @@ -571,9 +622,7 @@ inline auto gmtime(std::time_t time) -> std::tm { } template -inline auto gmtime( - std::chrono::time_point time_point) - -> std::tm { +inline auto gmtime(sys_time time_point) -> std::tm { return gmtime(detail::to_time_t(time_point)); } @@ -619,7 +668,8 @@ FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; + if (std::is_same::value) + return detail::use_utf8 ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; @@ -646,12 +696,10 @@ enum class numeric_system { // Glibc extensions for formatting numeric values. enum class pad_type { - unspecified, + // Pad a numeric result string with zeros (the default). + zero, // Do not pad a numeric result string. none, - // Pad a numeric result string with zeros even if the conversion specifier - // character uses space-padding by default. - zero, // Pad a numeric result string with spaces. space, }; @@ -659,7 +707,7 @@ enum class pad_type { template auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { if (pad == pad_type::none) return out; - return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); + return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); } template @@ -675,8 +723,8 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; - pad_type pad = pad_type::unspecified; while (ptr != end) { + pad_type pad = pad_type::zero; auto c = *ptr; if (c == '}') break; if (c != '%') { @@ -696,17 +744,11 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, pad = pad_type::none; ++ptr; break; - case '0': - pad = pad_type::zero; - ++ptr; - break; } if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case '%': - handler.on_text(ptr - 1, ptr); - break; + case '%': handler.on_text(ptr - 1, ptr); break; case 'n': { const Char newline[] = {'\n'}; handler.on_text(newline, newline + 1); @@ -718,145 +760,66 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, break; } // Year: - case 'Y': - handler.on_year(numeric_system::standard); - break; - case 'y': - handler.on_short_year(numeric_system::standard); - break; - case 'C': - handler.on_century(numeric_system::standard); - break; - case 'G': - handler.on_iso_week_based_year(); - break; - case 'g': - handler.on_iso_week_based_short_year(); - break; + case 'Y': handler.on_year(numeric_system::standard, pad); break; + case 'y': handler.on_short_year(numeric_system::standard); break; + case 'C': handler.on_century(numeric_system::standard); break; + case 'G': handler.on_iso_week_based_year(); break; + case 'g': handler.on_iso_week_based_short_year(); 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; + 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': - case 'h': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; - case 'm': - handler.on_dec_month(numeric_system::standard); - break; + case 'h': handler.on_abbr_month(); break; + case 'B': handler.on_full_month(); break; + case 'm': handler.on_dec_month(numeric_system::standard, pad); break; // Day of the year/month: case 'U': - handler.on_dec0_week_of_year(numeric_system::standard); + handler.on_dec0_week_of_year(numeric_system::standard, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::standard); - break; - case 'V': - handler.on_iso_week_of_year(numeric_system::standard); - break; - case 'j': - handler.on_day_of_year(); - break; - case 'd': - handler.on_day_of_month(numeric_system::standard); + handler.on_dec1_week_of_year(numeric_system::standard, pad); break; + case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; + case 'j': handler.on_day_of_year(pad); break; + case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::standard); + handler.on_day_of_month(numeric_system::standard, pad_type::space); break; // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::standard, pad); - break; - case 'M': - handler.on_minute(numeric_system::standard, pad); - break; - case 'S': - handler.on_second(numeric_system::standard, pad); - break; + case 'H': handler.on_24_hour(numeric_system::standard, pad); break; + case 'I': handler.on_12_hour(numeric_system::standard, pad); break; + case 'M': handler.on_minute(numeric_system::standard, pad); break; + case 'S': handler.on_second(numeric_system::standard, pad); 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(numeric_system::standard); - break; - case 'Z': - handler.on_tz_name(); - break; + 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(numeric_system::standard); 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 'Y': - handler.on_year(numeric_system::alternative); - break; - case 'y': - handler.on_offset_year(); - break; - case 'C': - handler.on_century(numeric_system::alternative); - break; - 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; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + case 'Y': handler.on_year(numeric_system::alternative, pad); break; + case 'y': handler.on_offset_year(); break; + case 'C': handler.on_century(numeric_system::alternative); break; + 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; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; } @@ -864,54 +827,34 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case 'y': - handler.on_short_year(numeric_system::alternative); - break; - case 'm': - handler.on_dec_month(numeric_system::alternative); - break; + case 'y': handler.on_short_year(numeric_system::alternative); break; + case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; case 'U': - handler.on_dec0_week_of_year(numeric_system::alternative); + handler.on_dec0_week_of_year(numeric_system::alternative, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::alternative); + handler.on_dec1_week_of_year(numeric_system::alternative, pad); break; case 'V': - handler.on_iso_week_of_year(numeric_system::alternative); + handler.on_iso_week_of_year(numeric_system::alternative, pad); break; case 'd': - handler.on_day_of_month(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::alternative); - break; - 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, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::alternative, pad); - break; - case 'M': - handler.on_minute(numeric_system::alternative, pad); + handler.on_day_of_month(numeric_system::alternative, pad_type::space); break; - case 'S': - handler.on_second(numeric_system::alternative, pad); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + 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, pad); break; + case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; + case 'M': handler.on_minute(numeric_system::alternative, pad); break; + case 'S': handler.on_second(numeric_system::alternative, pad); break; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; - default: - FMT_THROW(format_error("invalid format")); + default: FMT_THROW(format_error("invalid format")); } begin = ptr; } @@ -923,7 +866,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void unsupported() { static_cast(this)->unsupported(); } - FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_offset_year() { unsupported(); } FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } @@ -935,13 +878,20 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } - FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_year() { unsupported(); } - FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { + unsupported(); + } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } @@ -961,12 +911,21 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; -struct tm_format_checker : null_chrono_spec_handler { - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } +class tm_format_checker : public null_chrono_spec_handler { + private: + bool has_timezone_ = false; + + public: + constexpr explicit tm_format_checker(bool has_timezone) + : has_timezone_(has_timezone) {} + + FMT_NORETURN inline void unsupported() { + FMT_THROW(format_error("no format")); + } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_short_year(numeric_system) {} FMT_CONSTEXPR void on_offset_year() {} FMT_CONSTEXPR void on_century(numeric_system) {} @@ -978,13 +937,12 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_full_month() {} - FMT_CONSTEXPR void on_dec_month(numeric_system) {} - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_day_of_month(numeric_system) {} - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -998,8 +956,12 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset(numeric_system) {} - FMT_CONSTEXPR void on_tz_name() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } + FMT_CONSTEXPR void on_tz_name() { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } }; inline auto tm_wday_full_name(int wday) -> const char* { @@ -1029,41 +991,44 @@ inline auto tm_mon_short_name(int mon) -> const char* { } template -struct has_member_data_tm_gmtoff : std::false_type {}; +struct has_tm_gmtoff : std::false_type {}; template -struct has_member_data_tm_gmtoff> - : std::true_type {}; +struct has_tm_gmtoff> : std::true_type {}; -template -struct has_member_data_tm_zone : std::false_type {}; +template struct has_tm_zone : std::false_type {}; template -struct has_member_data_tm_zone> - : std::true_type {}; +struct has_tm_zone> : std::true_type {}; -#if FMT_USE_TZSET -inline void tzset_once() { - static bool init = []() -> bool { - _tzset(); - return true; - }(); - ignore_unused(init); +template ::value)> +bool set_tm_zone(T& time, char* tz) { + time.tm_zone = tz; + return true; +} +template ::value)> +bool set_tm_zone(T&, char*) { + return false; +} + +inline char* utc() { + static char tz[] = "UTC"; + return tz; } -#endif // Converts value to Int and checks that it's in the range [0, upper). template ::value)> inline auto to_nonnegative_int(T value, Int upper) -> Int { if (!std::is_unsigned::value && (value < 0 || to_unsigned(value) > to_unsigned(upper))) { - FMT_THROW(fmt::format_error("chrono value is out of range")); + FMT_THROW(format_error("chrono value is out of range")); } return static_cast(value); } template ::value)> inline auto to_nonnegative_int(T value, Int upper) -> Int { - if (value < 0 || value > static_cast(upper)) + auto int_value = static_cast(value); + if (int_value < 0 || value > static_cast(upper)) FMT_THROW(format_error("invalid value")); - return static_cast(value); + return int_value; } constexpr auto pow10(std::uint32_t n) -> long long { @@ -1098,16 +1063,16 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { using subsecond_precision = std::chrono::duration< typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; + std::ratio<1, pow10(num_fractional_digits)>>; - const auto fractional = d - fmt_duration_cast(d); + const auto fractional = d - detail::duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() - : fmt_duration_cast(fractional).count(); + : detail::duration_cast(fractional).count(); auto n = static_cast>(subseconds); - const int num_digits = detail::count_digits(n); + const int num_digits = count_digits(n); int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); if (precision < 0) { @@ -1115,28 +1080,31 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { if (std::ratio_less::value) { *out++ = '.'; - out = std::fill_n(out, leading_zeroes, '0'); - out = format_decimal(out, n, num_digits).end; + out = detail::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits); } - } else { + } else if (precision > 0) { *out++ = '.'; - leading_zeroes = (std::min)(leading_zeroes, precision); - out = std::fill_n(out, leading_zeroes, '0'); + leading_zeroes = min_of(leading_zeroes, precision); int remaining = precision - leading_zeroes; - if (remaining != 0 && remaining < num_digits) { - n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); - out = format_decimal(out, n, remaining).end; + out = detail::fill_n(out, leading_zeroes, '0'); + if (remaining < num_digits) { + int num_truncated_digits = num_digits - remaining; + n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); + if (n != 0) out = format_decimal(out, n, remaining); return; } - out = format_decimal(out, n, num_digits).end; - remaining -= num_digits; - out = std::fill_n(out, remaining, '0'); + if (n != 0) { + out = format_decimal(out, n, num_digits); + remaining -= num_digits; + } + out = detail::fill_n(out, remaining, '0'); } } // Format subseconds which are given as a floating point type with an // appropriate number of digits. We cannot pass the Duration here, as we -// explicitly need to pass the Rep value in the chrono_formatter. +// explicitly need to pass the Rep value in the duration_formatter. template void write_floating_seconds(memory_buffer& buf, Duration duration, int num_fractional_digits = -1) { @@ -1170,7 +1138,7 @@ class tm_writer { static constexpr int days_per_week = 7; const std::locale& loc_; - const bool is_classic_; + bool is_classic_; OutputIt out_; const Duration* subsecs_; const std::tm& tm_; @@ -1206,8 +1174,8 @@ class tm_writer { } auto tm_hour12() const noexcept -> int { - const auto h = tm_hour(); - const auto z = h < 12 ? h : h - 12; + auto h = tm_hour(); + auto z = h < 12 ? h : h - 12; return z == 0 ? 12 : z; } @@ -1223,11 +1191,11 @@ class tm_writer { // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { - const auto prev_year = curr_year - 1; - const auto curr_p = + auto prev_year = curr_year - 1; + auto curr_p = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week; - const auto prev_p = + auto prev_p = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week; return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); @@ -1237,15 +1205,15 @@ class tm_writer { days_per_week; } auto tm_iso_week_year() const noexcept -> long long { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return year - 1; if (w > iso_year_weeks(year)) return year + 1; return year; } auto tm_iso_week_of_year() const noexcept -> int { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return iso_year_weeks(year - 1); if (w > iso_year_weeks(year)) return 1; return w; @@ -1271,29 +1239,27 @@ class tm_writer { } } - void write_year_extended(long long year) { + void write_year_extended(long long year, pad_type pad) { // At least 4 characters. int width = 4; - if (year < 0) { - *out_++ = '-'; + bool negative = year < 0; + if (negative) { year = 0 - year; --width; } uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); - if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); - out_ = format_decimal(out_, n, num_digits).end; + if (negative && pad == pad_type::zero) *out_++ = '-'; + if (width > num_digits) + out_ = detail::write_padding(out_, pad, width - num_digits); + if (negative && pad != pad_type::zero) *out_++ = '-'; + out_ = format_decimal(out_, n, num_digits); } - void write_year(long long year) { - if (year >= 0 && year < 10000) { - write2(static_cast(year / 100)); - write2(static_cast(year % 100)); - } else { - write_year_extended(year); - } + void write_year(long long year, pad_type pad) { + write_year_extended(year, pad); } - void write_utc_offset(long offset, numeric_system ns) { + void write_utc_offset(long long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; @@ -1305,47 +1271,23 @@ class tm_writer { if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { + + template ::value)> + void format_utc_offset(const T& tm, numeric_system ns) { write_utc_offset(tm.tm_gmtoff, ns); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { -#if defined(_WIN32) && defined(_UCRT) -# if FMT_USE_TZSET - tzset_once(); -# endif - long offset = 0; - _get_timezone(&offset); - if (tm.tm_isdst) { - long dstbias = 0; - _get_dstbias(&dstbias); - offset += dstbias; - } - write_utc_offset(-offset, ns); -#else - if (ns == numeric_system::standard) return format_localized('z'); - - // Extract timezone offset from timezone conversion functions. - std::tm gtm = tm; - std::time_t gt = std::mktime(>m); - std::tm ltm = gmtime(gt); - std::time_t lt = std::mktime(<m); - long offset = gt - lt; - write_utc_offset(offset, ns); -#endif + template ::value)> + void format_utc_offset(const T&, numeric_system ns) { + write_utc_offset(0, ns); } - template ::value)> - void format_tz_name_impl(const T& tm) { - if (is_classic_) - out_ = write_tm_str(out_, tm.tm_zone, loc_); - else - format_localized('Z'); + template ::value)> + void format_tz_name(const T& tm) { + out_ = write_tm_str(out_, tm.tm_zone, loc_); } - template ::value)> - void format_tz_name_impl(const T&) { - format_localized('Z'); + template ::value)> + void format_tz_name(const T&) { + out_ = std::copy_n(utc(), 3, out_); } void format_localized(char format, char modifier = 0) { @@ -1364,7 +1306,7 @@ class tm_writer { auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - out_ = copy_str(begin, end, out_); + out_ = copy(begin, end, out_); } void on_abbr_weekday() { @@ -1411,11 +1353,11 @@ class tm_writer { *out_++ = ' '; on_abbr_month(); *out_++ = ' '; - on_day_of_month_space(numeric_system::standard); + on_day_of_month(numeric_system::standard, pad_type::space); *out_++ = ' '; on_iso_time(); *out_++ = ' '; - on_year(numeric_system::standard); + on_year(numeric_system::standard, pad_type::space); } else { format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); } @@ -1437,31 +1379,31 @@ class tm_writer { write_digit2_separated(buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); } void on_iso_date() { auto year = tm_year(); char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); + write2digits(buf, static_cast(year / 100)); } else { offset = 4; - write_year_extended(year); + write_year_extended(year, pad_type::zero); year = 0; } write_digit2_separated(buf + 2, static_cast(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-'); - out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + out_ = copy(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } - void on_tz_name() { format_tz_name_impl(tm_); } + void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } + void on_tz_name() { format_tz_name(tm_); } - void on_year(numeric_system ns) { + void on_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write_year(tm_year()); + return write_year(tm_year(), pad); format_localized('Y', 'E'); } void on_short_year(numeric_system ns) { @@ -1492,57 +1434,57 @@ class tm_writer { } } - void on_dec_month(numeric_system ns) { + void on_dec_month(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_mon() + 1); + return write2(tm_mon() + 1, pad); format_localized('m', 'O'); } - void on_dec0_week_of_year(numeric_system ns) { + void on_dec0_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, + pad); format_localized('U', 'O'); } - void on_dec1_week_of_year(numeric_system ns) { + void on_dec1_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week); + days_per_week, + pad); } else { format_localized('W', 'O'); } } - void on_iso_week_of_year(numeric_system ns) { + void on_iso_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_iso_week_of_year()); + return write2(tm_iso_week_of_year(), pad); format_localized('V', 'O'); } - void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_year() { + write_year(tm_iso_week_year(), pad_type::zero); + } void on_iso_week_based_short_year() { write2(split_year_lower(tm_iso_week_year())); } - void on_day_of_year() { + void on_day_of_year(pad_type pad) { auto yday = tm_yday() + 1; - write1(yday / 100); - write2(yday % 100); + auto digit1 = yday / 100; + if (digit1 != 0) + write1(digit1); + else + out_ = detail::write_padding(out_, pad); + write2(yday % 100, pad); } - void on_day_of_month(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + + void on_day_of_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mday(), pad); format_localized('d', 'O'); } - void on_day_of_month_space(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto mday = to_unsigned(tm_mday()) % 100; - const char* d2 = digits2(mday); - *out_++ = mday < 10 ? ' ' : d2[0]; - *out_++ = d2[1]; - } else { - format_localized('e', 'O'); - } - } void on_24_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) @@ -1569,7 +1511,7 @@ class tm_writer { write_floating_seconds(buf, *subsecs_); if (buf.size() > 1) { // Remove the leading "0", write something like ".123". - out_ = std::copy(buf.begin() + 1, buf.end(), out_); + out_ = copy(buf.begin() + 1, buf.end(), out_); } } else { write_fractional_seconds(out_, *subsecs_); @@ -1586,7 +1528,7 @@ class tm_writer { char buf[8]; write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); *out_++ = ' '; on_am_pm(); } else { @@ -1601,7 +1543,7 @@ class tm_writer { void on_iso_time() { on_24_hour_time(); *out_++ = ':'; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1621,11 +1563,11 @@ class tm_writer { struct chrono_format_checker : null_chrono_spec_handler { bool has_precision_integral = false; - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -1635,9 +1577,8 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() const { - if (has_precision_integral) { + if (has_precision_integral) FMT_THROW(format_error("precision not allowed for this argument type")); - } } FMT_CONSTEXPR void on_duration_unit() {} }; @@ -1672,22 +1613,20 @@ template ::value)> inline auto get_milliseconds(std::chrono::duration d) -> std::chrono::duration { - // this may overflow and/or the result may not fit in the - // target type. + // This may overflow and/or the result may not fit in the target type. #if FMT_SAFE_DURATION_CAST - using CommonSecondsType = + using common_seconds_type = typename std::common_type::type; - const auto d_as_common = fmt_duration_cast(d); - const auto d_as_whole_seconds = - fmt_duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = - fmt_duration_cast>(diff); + auto d_as_common = detail::duration_cast(d); + auto d_as_whole_seconds = + detail::duration_cast(d_as_common); + // This conversion should be nonproblematic. + auto diff = d_as_common - d_as_whole_seconds; + auto ms = detail::duration_cast>(diff); return ms; #else - auto s = fmt_duration_cast(d); - return fmt_duration_cast(d - s); + auto s = detail::duration_cast(d); + return detail::duration_cast(d - s); #endif } @@ -1700,16 +1639,16 @@ auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { template ::value)> auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { - auto specs = format_specs(); + auto specs = format_specs(); specs.precision = precision; - specs.type = precision >= 0 ? presentation_type::fixed_lower - : presentation_type::general_lower; + specs.set_type(precision >= 0 ? presentation_type::fixed + : presentation_type::general); return write(out, val, specs); } template auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { - return std::copy(unit.begin(), unit.end(), out); + return copy(unit.begin(), unit.end(), out); } template @@ -1717,7 +1656,7 @@ auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { // This works when wchar_t is UTF-32 because units only contain characters // that have the same representation in UTF-16 and UTF-32. utf8_to_utf16 u(unit); - return std::copy(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } template @@ -1743,44 +1682,40 @@ class get_locale { bool has_locale_ = false; public: - get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { if (localized) ::new (&locale_) std::locale(loc.template get()); } - ~get_locale() { + inline ~get_locale() { if (has_locale_) locale_.~locale(); } - operator const std::locale&() const { + inline operator const std::locale&() const { return has_locale_ ? locale_ : get_classic_locale(); } }; -template -struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; - bool localized = false; +template +struct duration_formatter { + using iterator = basic_appender; + iterator out; // rep is unsigned to avoid overflow. using rep = conditional_t::value && sizeof(Rep) < sizeof(int), unsigned, typename make_unsigned_or_unchanged::type>; rep val; + int precision; + locale_ref locale; + bool localized = false; using seconds = std::chrono::duration; seconds s; using milliseconds = std::chrono::duration; bool negative; - using char_type = typename FormatContext::char_type; - using tm_writer_type = tm_writer; + using tm_writer_type = tm_writer; - chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), - negative(false) { + duration_formatter(iterator o, std::chrono::duration d, + locale_ref loc) + : out(o), val(static_cast(d.count())), locale(loc), negative(false) { if (d.count() < 0) { val = 0 - val; negative = true; @@ -1789,24 +1724,21 @@ struct chrono_formatter { // this may overflow and/or the result may not fit in the // target type. // might need checked conversion (rep!=Rep) - s = fmt_duration_cast(std::chrono::duration(val)); + s = detail::duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. auto handle_nan_inf() -> bool { - if (isfinite(val)) { - return false; - } + if (isfinite(val)) return false; if (isnan(val)) { write_nan(); return true; } // must be +-inf - if (val > 0) { - write_pinf(); - } else { - write_ninf(); - } + if (val > 0) + std::copy_n("inf", 3, out); + else + std::copy_n("-inf", 4, out); return true; } @@ -1834,13 +1766,12 @@ struct chrono_formatter { } void write_sign() { - if (negative) { - *out++ = '-'; - negative = false; - } + if (!negative) return; + *out++ = '-'; + negative = false; } - void write(Rep value, int width, pad_type pad = pad_type::unspecified) { + void write(Rep value, int width, pad_type pad = pad_type::zero) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = @@ -1849,24 +1780,22 @@ struct chrono_formatter { if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } - out = format_decimal(out, n, num_digits).end; + 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); } template void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); - get_locale loc(localized, context.locale()); + get_locale loc(localized, locale); auto w = tm_writer_type(loc, out, time); (w.*cb)(args...); out = w.out(); } - void on_text(const char_type* begin, const char_type* end) { - std::copy(begin, end, out); + void on_text(const Char* begin, const Char* end) { + copy(begin, end, out); } // These are not implemented because durations don't have date information. @@ -1883,20 +1812,19 @@ struct chrono_formatter { void on_iso_date() {} void on_utc_offset(numeric_system) {} void on_tz_name() {} - void on_year(numeric_system) {} + void on_year(numeric_system, pad_type) {} void on_short_year(numeric_system) {} void on_offset_year() {} void on_century(numeric_system) {} void on_iso_week_based_year() {} void on_iso_week_based_short_year() {} - void on_dec_month(numeric_system) {} - void on_dec0_week_of_year(numeric_system) {} - void on_dec1_week_of_year(numeric_system) {} - void on_iso_week_of_year(numeric_system) {} - void on_day_of_month(numeric_system) {} - void on_day_of_month_space(numeric_system) {} - - void on_day_of_year() { + void on_dec_month(numeric_system, pad_type) {} + void on_dec0_week_of_year(numeric_system, pad_type) {} + void on_dec1_week_of_year(numeric_system, pad_type) {} + void on_iso_week_of_year(numeric_system, pad_type) {} + void on_day_of_month(numeric_system, pad_type) {} + + void on_day_of_year(pad_type) { if (handle_nan_inf()) return; write(days(), 0); } @@ -1937,13 +1865,12 @@ struct chrono_formatter { write_floating_seconds(buf, std::chrono::duration(val), precision); if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') { + if (buf.size() < 2 || buf[1] == '.') out = detail::write_padding(out, pad); - } - out = std::copy(buf.begin(), buf.end(), out); + out = copy(buf.begin(), buf.end(), out); } else { write(second(), 2, pad); - write_fractional_seconds( + write_fractional_seconds( out, std::chrono::duration(val), precision); } return; @@ -1974,7 +1901,7 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1985,94 +1912,245 @@ struct chrono_formatter { void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { - out = format_duration_unit(out); - } + void on_duration_unit() { out = format_duration_unit(out); } }; } // namespace detail #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 using weekday = std::chrono::weekday; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; +using year_month_day = std::chrono::year_month_day; #else // A fallback version of weekday. class weekday { private: - unsigned char value; + unsigned char value_; public: weekday() = default; - explicit constexpr weekday(unsigned wd) noexcept - : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned { return value; } + constexpr explicit weekday(unsigned wd) noexcept + : value_(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned { return value_; } }; -class year_month_day {}; -#endif +class day { + private: + unsigned char value_; + + public: + day() = default; + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; -// A rudimentary weekday formatter. -template struct formatter { +class month { private: - bool localized = false; + unsigned char value_; + + public: + month() = default; + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class year { + private: + int value_; + + public: + year() = default; + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } +}; + +class year_month_day { + private: + fmt::year year_; + fmt::month month_; + fmt::day day_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin != end && *begin == 'L') { - ++begin; - localized = true; + year_month_day() = default; + constexpr year_month_day(const year& y, const month& m, const day& d) noexcept + : year_(y), month_(m), day_(d) {} + constexpr auto year() const noexcept -> fmt::year { return year_; } + constexpr auto month() const noexcept -> fmt::month { return month_; } + constexpr auto day() const noexcept -> fmt::day { return day_; } +}; +#endif // __cpp_lib_chrono >= 201907 + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); } - return begin; + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); - detail::get_locale loc(localized, ctx.locale()); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); } }; +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(static_cast(d)); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + this->set_localized(); + } + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(static_cast(m)) - 1; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(this->localized(), ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_month(); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(y) - 1900; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_year(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year_month_day val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(val.year()) - 1900; + time.tm_mon = static_cast(static_cast(val.month())) - 1; + time.tm_mday = static_cast(static_cast(val.day())); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(true, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_iso_date(); + return w.out(); + } +}; + template struct formatter, Char> { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; detail::arg_ref precision_ref_; - bool localized_ = false; - basic_string_view format_str_; + basic_string_view fmt_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(), end = ctx.end(); if (it == end || *it == '}') return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } auto checker = detail::chrono_format_checker(); if (*it == '.') { checker.has_precision_integral = !std::is_floating_point::value; - it = detail::parse_precision(it, end, specs_.precision, precision_ref_, - ctx); + it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); } if (it != end && *it == 'L') { - localized_ = true; + specs_.set_localized(); ++it; } end = detail::parse_chrono_format(it, end, checker); - format_str_ = {it, detail::to_unsigned(end - it)}; + fmt_ = {it, detail::to_unsigned(end - it)}; return end; } @@ -2082,24 +2160,23 @@ struct formatter, Char> { auto specs = specs_; auto precision = specs.precision; specs.precision = -1; - auto begin = format_str_.begin(), end = format_str_.end(); + auto begin = fmt_.begin(), end = fmt_.end(); // As a possible future optimization, we could avoid extra copying if width // is not specified. auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - detail::handle_dynamic_spec(precision, - precision_ref_, ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), precision, + precision_ref_, ctx); if (begin == end || *begin == '}') { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); } else { - using chrono_formatter = - detail::chrono_formatter; - auto f = chrono_formatter(ctx, out, d); + auto f = + detail::duration_formatter(out, d, ctx.locale()); f.precision = precision; - f.localized = localized_; + f.localized = specs_.localized(); detail::parse_chrono_format(begin, end, f); } return detail::write( @@ -2107,130 +2184,143 @@ struct formatter, Char> { } }; -template -struct formatter, - Char> : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - using period = typename Duration::period; - if (detail::const_check( - period::num != 1 || period::den != 1 || - std::is_floating_point::value)) { - const auto epoch = val.time_since_epoch(); - auto subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); - - if (subsecs.count() < 0) { - auto second = - detail::fmt_duration_cast(std::chrono::seconds(1)); - if (epoch.count() < ((Duration::min)() + second).count()) - FMT_THROW(format_error("duration is too small")); - subsecs += second; - val -= second; - } - - return formatter::do_format(gmtime(val), ctx, &subsecs); - } +template struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + basic_string_view fmt_ = + detail::string_literal(); - return formatter::format(gmtime(val), ctx); - } -}; + protected: + auto localized() const -> bool { return specs_.localized(); } + FMT_CONSTEXPR void set_localized() { specs_.set_localized(); } -#if FMT_USE_LOCAL_TIME -template -struct formatter, Char> - : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } + FMT_CONSTEXPR auto do_parse(parse_context& ctx, bool has_timezone) + -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; - template - auto format(std::chrono::local_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { - using period = typename Duration::period; - if (period::num != 1 || period::den != 1 || - std::is_floating_point::value) { - const auto epoch = val.time_since_epoch(); - const auto subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); + it = detail::parse_align(it, end, specs_); + if (it == end) return it; - return formatter::do_format(localtime(val), ctx, &subsecs); + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; } - return formatter::format(localtime(val), ctx); - } -}; -#endif + if (*it == 'L') { + specs_.set_localized(); + ++it; + } -#if FMT_USE_UTC_TIME -template -struct formatter, - Char> - : formatter, - Char> { - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter< - std::chrono::time_point, - Char>::format(std::chrono::utc_clock::to_sys(val), ctx); + end = detail::parse_chrono_format(it, end, + detail::tm_format_checker(has_timezone)); + // Replace the default format string only if the new spec is not empty. + if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; + return end; } -}; -#endif - -template struct formatter { - private: - format_specs specs_; - detail::arg_ref width_ref_; - - protected: - basic_string_view format_str_; - template + template auto do_format(const std::tm& tm, FormatContext& ctx, const Duration* subsecs) const -> decltype(ctx.out()) { auto specs = specs_; auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); - auto loc_ref = ctx.locale(); + auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref(); detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = - detail::tm_writer(loc, out, tm, subsecs); - detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w); + auto w = detail::tm_writer, Char, Duration>( + loc, out, tm, subsecs); + detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto it = ctx.begin(), end = ctx.end(); - if (it == end || *it == '}') return it; + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, detail::has_tm_gmtoff::value); + } - it = detail::parse_align(it, end, specs_); - if (it == end) return it; + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + return do_format(tm, ctx, nullptr); + } +}; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; +// DEPRECATED! Reversed order of template parameters. +template +struct formatter, Char> : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, true); + } - end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); - // Replace the default format_str only if the new spec is not empty. - if (end != it) format_str_ = {it, detail::to_unsigned(end - it)}; - return end; + template + auto format(sys_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + std::tm tm = gmtime(val); + using period = typename Duration::period; + if (detail::const_check( + period::num == 1 && period::den == 1 && + !std::is_floating_point::value)) { + detail::set_tm_zone(tm, detail::utc()); + return formatter::format(tm, ctx); + } + Duration epoch = val.time_since_epoch(); + Duration subsecs = detail::duration_cast( + epoch - detail::duration_cast(epoch)); + if (subsecs.count() < 0) { + auto second = detail::duration_cast(std::chrono::seconds(1)); + if (tm.tm_sec != 0) { + --tm.tm_sec; + } else { + tm = gmtime(val - second); + detail::set_tm_zone(tm, detail::utc()); + } + subsecs += second; + } + return formatter::do_format(tm, ctx, &subsecs); } +}; +template +struct formatter, Char> + : formatter, Char> { template - auto format(const std::tm& tm, FormatContext& ctx) const + auto format(utc_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format( + detail::utc_clock::to_sys(val), ctx); + } +}; + +template +struct formatter, Char> + : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, false); + } + + template + auto format(local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { - return do_format(tm, ctx, nullptr); + auto time_since_epoch = val.time_since_epoch(); + auto seconds_since_epoch = + detail::duration_cast(time_since_epoch); + // Use gmtime to prevent time zone conversion since local_time has an + // unspecified time zone. + std::tm t = gmtime(seconds_since_epoch.count()); + using period = typename Duration::period; + if (period::num == 1 && period::den == 1 && + !std::is_floating_point::value) { + return formatter::format(t, ctx); + } + auto subsecs = + detail::duration_cast(time_since_epoch - seconds_since_epoch); + return formatter::do_format(t, ctx, &subsecs); } }; diff --git a/deps/spdlog/include/spdlog/fmt/bundled/color.h b/deps/spdlog/include/spdlog/fmt/bundled/color.h index 367849a86a7..638f15b43f3 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/color.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/color.h @@ -190,11 +190,11 @@ enum class emphasis : uint8_t { // 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) + constexpr rgb() : r(0), g(0), b(0) {} + constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + constexpr rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} - FMT_CONSTEXPR rgb(color hex) + constexpr rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), b(uint32_t(hex) & 0xFF) {} @@ -205,97 +205,135 @@ struct rgb { namespace detail { -// color is a struct of either a rgb color or a terminal color. +// A bit-packed variant of an RGB color, a terminal color, or unset color. +// see text_style for the bit-packing scheme. struct color_type { - FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { - value.rgb_color = static_cast(rgb_color); + constexpr color_type() noexcept = default; + constexpr color_type(color rgb_color) noexcept + : value_(static_cast(rgb_color) | (1 << 24)) {} + constexpr color_type(rgb rgb_color) noexcept + : color_type(static_cast( + (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} + constexpr color_type(terminal_color term_color) noexcept + : value_(static_cast(term_color) | (3 << 24)) {} + + constexpr auto is_terminal_color() const noexcept -> bool { + return (value_ & (1 << 25)) != 0; } - FMT_CONSTEXPR color_type(rgb rgb_color) 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) noexcept - : is_rgb(), value{} { - value.term_color = static_cast(term_color); + + constexpr auto value() const noexcept -> uint32_t { + return value_ & 0xFFFFFF; } - bool is_rgb; - union color_union { - uint8_t term_color; - uint32_t rgb_color; - } value; + + constexpr color_type(uint32_t value) noexcept : value_(value) {} + + uint32_t value_ = 0; }; } // namespace detail -/** A text style consisting of foreground and background colors and emphasis. */ +/// A text style consisting of foreground and background colors and emphasis. class text_style { + // The information is packed as follows: + // ┌──┐ + // │ 0│─┐ + // │..│ ├── foreground color value + // │23│─┘ + // ├──┤ + // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's + // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) + // ├──┤ + // │26│──── overflow bit, always zero (see below) + // ├──┤ + // │27│─┐ + // │..│ │ + // │50│ │ + // ├──┤ │ + // │51│ ├── background color (same format as the foreground color) + // │52│ │ + // ├──┤ │ + // │53│─┘ + // ├──┤ + // │54│─┐ + // │..│ ├── emphases + // │61│─┘ + // ├──┤ + // │62│─┬── unused + // │63│─┘ + // └──┘ + // The overflow bits are there to make operator|= efficient. + // When ORing, we must throw if, for either the foreground or background, + // one style specifies a terminal color and the other specifies any color + // (terminal or RGB); in other words, if one discriminator is 11 and the + // other is 11 or 01. + // + // We do that check by adding the styles. Consider what adding does to each + // possible pair of discriminators: + // 00 + 00 = 000 + // 01 + 00 = 001 + // 11 + 00 = 011 + // 01 + 01 = 010 + // 11 + 01 = 100 (!!) + // 11 + 11 = 110 (!!) + // In the last two cases, the ones we want to catch, the third bit——the + // overflow bit——is set. Bingo. + // + // We must take into account the possible carry bit from the bits + // before the discriminator. The only potentially problematic case is + // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry + // bit is impossible in that case, because 00 (unset color) means the + // 24 bits that precede the discriminator are all zero. + // + // This test can be applied to both colors simultaneously. + public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept - : set_foreground_color(), set_background_color(), ems(em) {} - - FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& { - 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; - } + : style_(static_cast(em) << 54) {} - ems = static_cast(static_cast(ems) | - static_cast(rhs.ems)); + FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { + if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) + report_error("can't OR a terminal color"); + style_ |= rhs.style_; return *this; } - friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs) + friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) -> text_style { return lhs |= rhs; } + FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { + return style_ == rhs.style_; + } + + FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { + return !(*this == rhs); + } + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { - return set_foreground_color; + return (style_ & (1 << 24)) != 0; } FMT_CONSTEXPR auto has_background() const noexcept -> bool { - return set_background_color; + return (style_ & (1ULL << 51)) != 0; } FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { - return static_cast(ems) != 0; + return (style_ >> 54) != 0; } FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); - return foreground_color; + return style_ & 0x3FFFFFF; } FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); - return background_color; + return (style_ >> 27) & 0x3FFFFFF; } FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); - return ems; + return static_cast(style_ >> 54); } private: - FMT_CONSTEXPR text_style(bool is_foreground, - detail::color_type text_color) 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; - } - } + FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept -> text_style; @@ -303,23 +341,19 @@ class text_style { friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept -> text_style; - detail::color_type foreground_color; - detail::color_type background_color; - bool set_foreground_color; - bool set_background_color; - emphasis ems; + uint64_t style_ = 0; }; -/** Creates a text style from the foreground (text) color. */ +/// Creates a text style from the foreground (text) color. FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept -> text_style { - return text_style(true, foreground); + return foreground.value_; } -/** Creates a text style from the background color. */ +/// Creates a text style from the background color. FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept -> text_style { - return text_style(false, background); + return static_cast(background.value_) << 27; } FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept @@ -330,13 +364,13 @@ FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept namespace detail { template struct ansi_color_escape { - FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + FMT_CONSTEXPR ansi_color_escape(color_type text_color, const char* esc) noexcept { // If we have a terminal color, we need to output another escape code // sequence. - if (!text_color.is_rgb) { + if (text_color.is_terminal_color()) { bool is_background = esc == string_view("\x1b[48;2;"); - uint32_t value = text_color.value.term_color; + uint32_t value = text_color.value(); // Background ASCII codes are the same as the foreground ones but with // 10 more. if (is_background) value += 10u; @@ -360,7 +394,7 @@ template struct ansi_color_escape { for (int i = 0; i < 7; i++) { buffer[i] = static_cast(esc[i]); } - rgb color(text_color.value.rgb_color); + rgb color(text_color.value()); to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); @@ -390,8 +424,8 @@ template struct ansi_color_escape { FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* { - return buffer + std::char_traits::length(buffer); + FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { + return buffer + basic_string_view(buffer).size(); } private: @@ -412,13 +446,13 @@ template struct ansi_color_escape { }; template -FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept +FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept -> ansi_color_escape { return ansi_color_escape(foreground, "\x1b[38;2;"); } template -FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept +FMT_CONSTEXPR auto make_background_color(color_type background) noexcept -> ansi_color_escape { return ansi_color_escape(background, "\x1b[48;2;"); } @@ -434,153 +468,116 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg : detail::view { +template struct styled_arg : view { const T& value; text_style style; styled_arg(const T& v, text_style s) : value(v), style(s) {} }; template -void vformat_to(buffer& buf, const text_style& ts, - basic_string_view format_str, - basic_format_args>> args) { - bool has_style = false; +void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, + basic_format_args> args) { if (ts.has_emphasis()) { - has_style = true; - auto emphasis = detail::make_emphasis(ts.get_emphasis()); + auto emphasis = make_emphasis(ts.get_emphasis()); buf.append(emphasis.begin(), emphasis.end()); } if (ts.has_foreground()) { - has_style = true; - auto foreground = detail::make_foreground_color(ts.get_foreground()); + auto foreground = make_foreground_color(ts.get_foreground()); buf.append(foreground.begin(), foreground.end()); } if (ts.has_background()) { - has_style = true; - auto background = detail::make_background_color(ts.get_background()); + auto background = make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - detail::vformat_to(buf, format_str, args, {}); - if (has_style) detail::reset_color(buf); + vformat_to(buf, fmt, args); + if (ts != text_style()) reset_color(buf); } - } // namespace detail -inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, - format_args args) { - // Legacy wide streams are not supported. +inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); - if (detail::is_utf8()) { - detail::print(f, string_view(buf.begin(), buf.size())); - return; - } - buf.push_back('\0'); - int result = std::fputs(buf.data(), f); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); + print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); } /** - \rst - 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); - \endrst + * 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) { - vprint(f, ts, format_str, - fmt::make_format_args>>(args...)); +template +void print(FILE* f, text_style ts, format_string fmt, T&&... args) { + vprint(f, ts, fmt.str, vargs{{args...}}); } /** - \rst - 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); - \endrst + * 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 +void print(text_style ts, format_string fmt, T&&... args) { + return print(stdout, ts, fmt, std::forward(args)...); } -template > -inline auto vformat( - const text_style& ts, const S& format_str, - basic_format_args>> args) - -> std::basic_string { - basic_memory_buffer buf; - detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); +inline auto vformat(text_style ts, string_view fmt, format_args args) + -> std::string { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, 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 auto format(const text_style& ts, const S& format_str, - const Args&... args) -> std::basic_string { - return fmt::vformat(ts, detail::to_string_view(format_str), - fmt::make_format_args>(args...)); + * 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); + * ``` + */ +template +inline auto format(text_style ts, format_string fmt, T&&... args) + -> std::string { + return fmt::vformat(ts, fmt.str, vargs{{args...}}); } -/** - Formats a string with the given text_style and writes the output to ``out``. - */ -template ::value)> -auto vformat_to(OutputIt out, const text_style& ts, - basic_string_view format_str, - basic_format_args>> args) +/// Formats a string with the given text_style and writes the output to `out`. +template ::value)> +auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) -> OutputIt { - auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, ts, format_str, args); + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, fmt, args); return detail::get_iterator(buf, out); } /** - \rst - Formats arguments with the given text_style, writes the result to the output - iterator ``out`` and returns the iterator past the end of the output range. - - **Example**:: - - std::vector out; - fmt::format_to(std::back_inserter(out), - fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); - \endrst -*/ -template < - typename OutputIt, typename S, typename... Args, - bool enable = detail::is_output_iterator>::value && - detail::is_string::value> -inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, - Args&&... args) -> - typename std::enable_if::type { - return vformat_to(out, ts, detail::to_string_view(format_str), - fmt::make_format_args>>(args...)); + * Formats arguments with the given text style, writes the result to the output + * iterator `out` and returns the iterator past the end of the output range. + * + * **Example**: + * + * std::vector out; + * fmt::format_to(std::back_inserter(out), + * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + */ +template ::value)> +inline auto format_to(OutputIt out, text_style ts, format_string fmt, + T&&... args) -> OutputIt { + return vformat_to(out, ts, fmt.str, vargs{{args...}}); } template @@ -589,47 +586,44 @@ struct formatter, Char> : formatter { auto format(const detail::styled_arg& arg, FormatContext& ctx) const -> decltype(ctx.out()) { const auto& ts = arg.style; - const auto& value = arg.value; auto out = ctx.out(); bool has_style = false; if (ts.has_emphasis()) { has_style = true; auto emphasis = detail::make_emphasis(ts.get_emphasis()); - out = std::copy(emphasis.begin(), emphasis.end(), out); + out = detail::copy(emphasis.begin(), emphasis.end(), out); } if (ts.has_foreground()) { has_style = true; auto foreground = detail::make_foreground_color(ts.get_foreground()); - out = std::copy(foreground.begin(), foreground.end(), out); + out = detail::copy(foreground.begin(), foreground.end(), out); } if (ts.has_background()) { has_style = true; auto background = detail::make_background_color(ts.get_background()); - out = std::copy(background.begin(), background.end(), out); + out = detail::copy(background.begin(), background.end(), out); } - out = formatter::format(value, ctx); + out = formatter::format(arg.value, ctx); if (has_style) { auto reset_color = string_view("\x1b[0m"); - out = std::copy(reset_color.begin(), reset_color.end(), out); + out = detail::copy(reset_color.begin(), reset_color.end(), out); } return out; } }; /** - \rst - Returns an argument that will be formatted using ANSI escape sequences, - to be used in a formatting function. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::color::green) | - fmt::bg(fmt::color::blue))); - \endrst + * Returns an argument that will be formatted using ANSI escape sequences, + * to be used in a formatting function. + * + * **Example**: + * + * fmt::print("Elapsed time: {0:.2f} seconds", + * fmt::styled(1.23, fmt::fg(fmt::color::green) | + * fmt::bg(fmt::color::blue))); */ template FMT_CONSTEXPR auto styled(const T& value, text_style ts) diff --git a/deps/spdlog/include/spdlog/fmt/bundled/compile.h b/deps/spdlog/include/spdlog/fmt/bundled/compile.h index 3b3f166e0cd..08d9427ff24 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/compile.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/compile.h @@ -8,54 +8,39 @@ #ifndef FMT_COMPILE_H_ #define FMT_COMPILE_H_ +#ifndef FMT_MODULE +# include // std::back_inserter +#endif + #include "format.h" FMT_BEGIN_NAMESPACE -namespace detail { - -template -FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end, - counting_iterator it) -> counting_iterator { - return it + (end - begin); -} // A compile-time string which is compiled into fast formatting code. -class compiled_string {}; +FMT_EXPORT class compiled_string {}; template struct is_compiled_string : std::is_base_of {}; -/** - \rst - Converts a string literal *s* into a format string that will be parsed at - compile time and converted into efficient formatting code. Requires C++17 - ``constexpr if`` compiler support. - - **Example**:: +namespace detail { - // Converts 42 into std::string using the most efficient method and no - // runtime format string processing. - std::string s = fmt::format(FMT_COMPILE("{}"), 42); - \endrst +/** + * Converts a string literal `s` into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires C++17 + * `constexpr if` compiler support. + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * std::string s = fmt::format(FMT_COMPILE("{}"), 42); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -# define FMT_COMPILE(s) \ - FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -template Str> -struct udl_compiled_string : compiled_string { - using char_type = Char; - explicit constexpr operator basic_string_view() const { - return {Str.data, N - 1}; - } -}; -#endif - template auto first(const T& value, const Tail&...) -> const T& { return value; @@ -75,6 +60,29 @@ constexpr const auto& get([[maybe_unused]] const T& first, return detail::get(rest...); } +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_static_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return -1; +} +# endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +# endif + (void)name; + return -1; +} + template constexpr int get_arg_index_by_name(basic_string_view name, type_list) { @@ -144,11 +152,12 @@ template struct field { template constexpr OutputIt format(OutputIt out, const Args&... args) const { const T& arg = get_arg_checked(args...); - if constexpr (std::is_convertible_v>) { + if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); - return copy_str(s.begin(), s.end(), out); + return copy(s.begin(), s.end(), out); + } else { + return write(out, arg); } - return write(out, arg); } }; @@ -236,13 +245,12 @@ constexpr size_t parse_text(basic_string_view str, size_t pos) { } template -constexpr auto compile_format_string(S format_str); +constexpr auto compile_format_string(S fmt); template -constexpr auto parse_tail(T head, S format_str) { - if constexpr (POS != - basic_string_view(format_str).size()) { - constexpr auto tail = compile_format_string(format_str); +constexpr auto parse_tail(T head, S fmt) { + if constexpr (POS != basic_string_view(fmt).size()) { + constexpr auto tail = compile_format_string(fmt); if constexpr (std::is_same, unknown_format>()) return tail; @@ -274,6 +282,7 @@ constexpr parse_specs_result parse_specs(basic_string_view str, } template struct arg_id_handler { + arg_id_kind kind; arg_ref arg_id; constexpr int on_auto() { @@ -281,25 +290,28 @@ template struct arg_id_handler { return 0; } constexpr int on_index(int id) { + kind = arg_id_kind::index; arg_id = arg_ref(id); return 0; } constexpr int on_name(basic_string_view id) { + kind = arg_id_kind::name; arg_id = arg_ref(id); return 0; } }; template struct parse_arg_id_result { + arg_id_kind kind; arg_ref arg_id; const Char* arg_id_end; }; template constexpr auto parse_arg_id(const Char* begin, const Char* end) { - auto handler = arg_id_handler{arg_ref{}}; + auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; auto arg_id_end = parse_arg_id(begin, end, handler); - return parse_arg_id_result{handler.arg_id, arg_id_end}; + return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; } template struct field_type { @@ -313,14 +325,13 @@ struct field_type::value>> { template -constexpr auto parse_replacement_field_then_tail(S format_str) { +constexpr auto parse_replacement_field_then_tail(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail( - field::type, ARG_INDEX>(), - format_str); + field::type, ARG_INDEX>(), fmt); } else if constexpr (c != ':') { FMT_THROW(format_error("expected ':'")); } else { @@ -333,7 +344,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { return parse_tail( spec_field::type, ARG_INDEX>{ result.fmt}, - format_str); + fmt); } } } @@ -341,22 +352,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { // 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) { +constexpr auto compile_format_string(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); if constexpr (str[POS] == '{') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '{' in format string")); if constexpr (str[POS + 1] == '{') { - return parse_tail(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { static_assert(ID != manual_indexing_id, "cannot switch from manual to automatic argument indexing"); constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail, Args, - POS + 1, ID, next_id>( - format_str); + POS + 1, ID, next_id>(fmt); } else { constexpr auto arg_id_result = parse_arg_id(str.data() + POS + 1, str.data() + str.size()); @@ -364,28 +374,27 @@ constexpr auto compile_format_string(S format_str) { constexpr char_type c = arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); static_assert(c == '}' || c == ':', "missing '}' in format string"); - if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { + if constexpr (arg_id_result.kind == arg_id_kind::index) { static_assert( ID == manual_indexing_id || ID == 0, "cannot switch from automatic to manual argument indexing"); - constexpr auto arg_index = arg_id_result.arg_id.val.index; + constexpr auto arg_index = arg_id_result.arg_id.index; return parse_replacement_field_then_tail, Args, arg_id_end_pos, arg_index, manual_indexing_id>( - format_str); - } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + fmt); + } else if constexpr (arg_id_result.kind == arg_id_kind::name) { constexpr auto arg_index = - get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); + get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); if constexpr (arg_index >= 0) { constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail< decltype(get_type::value), Args, arg_id_end_pos, - arg_index, next_id>(format_str); + arg_index, next_id>(fmt); } else if constexpr (c == '}') { return parse_tail( - runtime_named_field{arg_id_result.arg_id.val.name}, - format_str); + runtime_named_field{arg_id_result.arg_id.name}, fmt); } else if constexpr (c == ':') { return unknown_format(); // no type info for specs parsing } @@ -394,29 +403,26 @@ constexpr auto compile_format_string(S format_str) { } else if constexpr (str[POS] == '}') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '}' in format string")); - return parse_tail(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } else { constexpr auto end = parse_text(str, POS + 1); if constexpr (end - POS > 1) { - return parse_tail(make_text(str, POS, end - POS), - format_str); + return parse_tail(make_text(str, POS, end - POS), fmt); } else { - return parse_tail(code_unit{str[POS]}, - format_str); + return parse_tail(code_unit{str[POS]}, fmt); } } } template ::value)> -constexpr auto compile(S format_str) { - constexpr auto str = basic_string_view(format_str); + FMT_ENABLE_IF(is_compiled_string::value)> +constexpr auto compile(S fmt) { + constexpr auto str = basic_string_view(fmt); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { constexpr auto result = - detail::compile_format_string, 0, 0>( - format_str); + detail::compile_format_string, 0, 0>(fmt); return result; } } @@ -445,7 +451,7 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, } template ::value)> + FMT_ENABLE_IF(is_compiled_string::value)> FMT_INLINE std::basic_string format(const S&, Args&&... args) { if constexpr (std::is_same::value) { @@ -472,7 +478,7 @@ FMT_INLINE std::basic_string format(const S&, } template ::value)> + FMT_ENABLE_IF(is_compiled_string::value)> FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, @@ -487,44 +493,42 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { #endif template ::value)> -auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) + FMT_ENABLE_IF(is_compiled_string::value)> +auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - fmt::format_to(std::back_inserter(buf), format_str, - std::forward(args)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } template ::value)> -FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) + FMT_ENABLE_IF(is_compiled_string::value)> +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) -> size_t { - return fmt::format_to(detail::counting_iterator(), format_str, args...) - .count(); + auto buf = detail::counting_buffer<>(); + fmt::format_to(appender(buf), fmt, args...); + return buf.count(); } template ::value)> -void print(std::FILE* f, const S& format_str, const Args&... args) { - memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), format_str, args...); - detail::print(f, {buffer.data(), buffer.size()}); + FMT_ENABLE_IF(is_compiled_string::value)> +void print(std::FILE* f, const S& fmt, const Args&... args) { + auto buf = memory_buffer(); + fmt::format_to(appender(buf), fmt, args...); + detail::print(f, {buf.data(), buf.size()}); } template ::value)> -void print(const S& format_str, const Args&... args) { - print(stdout, format_str, args...); + FMT_ENABLE_IF(is_compiled_string::value)> +void print(const S& fmt, const Args&... args) { + print(stdout, fmt, args...); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { -template constexpr auto operator""_cf() { - using char_t = remove_cvref_t; - return detail::udl_compiled_string(); +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); } } // namespace literals #endif diff --git a/deps/spdlog/include/spdlog/fmt/bundled/core.h b/deps/spdlog/include/spdlog/fmt/bundled/core.h index b51c1406a99..8ca735f0c00 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/core.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/core.h @@ -1,2969 +1,5 @@ -// Formatting library for C++ - the core API for char/UTF-8 -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. +// This file is only provided for compatibility and may be removed in future +// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h +// otherwise. -#ifndef FMT_CORE_H_ -#define FMT_CORE_H_ - -#include // std::byte -#include // std::FILE -#include // std::strlen -#include -#include -#include // std::addressof -#include -#include - -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100201 - -#if defined(__clang__) && !defined(__ibmxl__) -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else -# define FMT_CLANG_VERSION 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ - !defined(__NVCOMPILER) -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#else -# define FMT_GCC_VERSION 0 -#endif - -#ifndef FMT_GCC_PRAGMA -// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. -# if FMT_GCC_VERSION >= 504 -# define FMT_GCC_PRAGMA(arg) _Pragma(arg) -# else -# define FMT_GCC_PRAGMA(arg) -# endif -#endif - -#ifdef __ICL -# define FMT_ICC_VERSION __ICL -#elif defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#else -# define FMT_ICC_VERSION 0 -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VERSION _MSC_VER -# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) -#else -# define FMT_MSC_VERSION 0 -# define FMT_MSC_WARNING(...) -#endif - -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 -# 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 - -#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -// Check if relaxed C++14 constexpr is supported. -// GCC doesn't allow throw in constexpr until version 6 (bug 67371). -#ifndef FMT_USE_CONSTEXPR -# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ - (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ - !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L) -# define FMT_USE_CONSTEXPR 1 -# else -# define FMT_USE_CONSTEXPR 0 -# endif -#endif -#if FMT_USE_CONSTEXPR -# define FMT_CONSTEXPR constexpr -#else -# define FMT_CONSTEXPR -#endif - -#if (FMT_CPLUSPLUS >= 202002L || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \ - ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \ - (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \ - (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1928)) && \ - defined(__cpp_lib_is_constant_evaluated) -# define FMT_CONSTEXPR20 constexpr -#else -# define FMT_CONSTEXPR20 -#endif - -// Check if constexpr std::char_traits<>::{compare,length} are supported. -#if defined(__GLIBCXX__) -# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ - _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#endif -#ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS -#endif - -// Check if exceptions are disabled. -#ifndef FMT_EXCEPTIONS -# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -# else -# define FMT_EXCEPTIONS 1 -# endif -#endif - -// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ - !defined(__NVCC__) -# define FMT_NORETURN [[noreturn]] -#else -# define FMT_NORETURN -#endif - -#ifndef FMT_NODISCARD -# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) -# define FMT_NODISCARD [[nodiscard]] -# else -# define FMT_NODISCARD -# endif -#endif - -#ifndef FMT_INLINE -# if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_INLINE inline __attribute__((always_inline)) -# else -# define FMT_INLINE inline -# endif -#endif - -#ifdef _MSC_VER -# define FMT_UNCHECKED_ITERATOR(It) \ - using _Unchecked_type = It // Mark iterator as checked. -#else -# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It -#endif - -#ifndef FMT_BEGIN_NAMESPACE -# define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ - inline namespace v10 { -# define FMT_END_NAMESPACE \ - } \ - } -#endif - -#ifndef FMT_EXPORT -# define FMT_EXPORT -# define FMT_BEGIN_EXPORT -# define FMT_END_EXPORT -#endif - -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_VISIBILITY(value) __attribute__((visibility(value))) -#else -# define FMT_VISIBILITY(value) -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# if defined(FMT_LIB_EXPORT) -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -# define FMT_API FMT_VISIBILITY("default") -#endif -#ifndef FMT_API -# define FMT_API -#endif - -// libc++ supports string_view in pre-c++17. -#if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -# include -# define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW -#endif - -#ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VERSION -#endif - -#ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - (!defined(__apple_build_version__) || \ - __apple_build_version__ >= 14000029L) && \ - FMT_CPLUSPLUS >= 202002L) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1929)) -// consteval is broken in MSVC before VS2019 version 16.10 and Apple clang -// before 14. -# define FMT_CONSTEVAL consteval -# define FMT_HAS_CONSTEVAL -# else -# define FMT_CONSTEVAL -# endif -#endif - -#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) && \ - !defined(__NVCOMPILER) && !defined(__LCC__) -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -# else -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 -# endif -#endif - -// GCC < 5 requires this-> in decltype -#ifndef FMT_DECLTYPE_THIS -# if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -# define FMT_DECLTYPE_THIS this-> -# else -# define FMT_DECLTYPE_THIS -# endif -#endif - -// Enable minimal optimizations for more compact code in debug mode. -FMT_GCC_PRAGMA("GCC push_options") -#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ - !defined(__CUDACC__) -FMT_GCC_PRAGMA("GCC optimize(\"Og\")") -#endif - -FMT_BEGIN_NAMESPACE - -// Implementations of enable_if_t and other metafunctions for older 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; -template struct type_identity { - using type = T; -}; -template using type_identity_t = typename type_identity::type; -template -using underlying_t = typename std::underlying_type::type; - -// Checks whether T is a container with contiguous storage. -template struct is_contiguous : std::false_type {}; -template -struct is_contiguous> : std::true_type {}; - -struct monostate { - constexpr 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). -#ifdef FMT_DOC -# define FMT_ENABLE_IF(...) -#else -# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 -#endif - -// This is defined in core.h instead of format.h to avoid injecting in std. -// It is a template to avoid undesirable implicit conversions to std::byte. -#ifdef __cpp_lib_byte -template ::value)> -inline auto format_as(T b) -> unsigned char { - return static_cast(b); -} -#endif - -namespace detail { -// Suppresses "unused variable" warnings with the method described in -// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -// (void)var does not work on many Intel compilers. -template FMT_CONSTEXPR void ignore_unused(const T&...) {} - -constexpr FMT_INLINE auto is_constant_evaluated( - bool default_value = false) noexcept -> bool { -// Workaround for incompatibility between libstdc++ consteval-based -// std::is_constant_evaluated() implementation and clang-14. -// https://github.com/fmtlib/fmt/issues/3247 -#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 12 && \ - (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) - ignore_unused(default_value); - return __builtin_is_constant_evaluated(); -#elif defined(__cpp_lib_is_constant_evaluated) - ignore_unused(default_value); - return std::is_constant_evaluated(); -#else - return default_value; -#endif -} - -// Suppresses "conditional expression is constant" warnings. -template constexpr FMT_INLINE auto const_check(T value) -> T { - return value; -} - -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - const char* message); - -#ifndef FMT_ASSERT -# ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Wempty-body. -# define FMT_ASSERT(condition, message) \ - fmt::detail::ignore_unused((condition), (message)) -# else -# define FMT_ASSERT(condition, message) \ - ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ - ? (void)0 \ - : fmt::detail::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__) && !defined(__NVCC__) && \ - !(FMT_CLANG_VERSION && FMT_MSC_VERSION) -# define FMT_USE_INT128 1 -using int128_opt = __int128_t; // An optional native 128-bit integer. -using uint128_opt = __uint128_t; -template inline auto convert_for_visit(T value) -> T { - return value; -} -#else -# define FMT_USE_INT128 0 -#endif -#if !FMT_USE_INT128 -enum class int128_opt {}; -enum class uint128_opt {}; -// Reduce template instantiations. -template auto convert_for_visit(T) -> monostate { return {}; } -#endif - -// Casts a nonnegative integer to unsigned. -template -FMT_CONSTEXPR auto to_unsigned(Int value) -> - typename std::make_unsigned::type { - FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); - return static_cast::type>(value); -} - -FMT_CONSTEXPR inline auto is_utf8() -> bool { - FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; - - // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). - using uchar = unsigned char; - return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && - uchar(section[1]) == 0xA7); -} -} // namespace detail - -/** - 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). - */ -FMT_EXPORT -template class basic_string_view { - private: - const Char* data_; - size_t size_; - - public: - using value_type = Char; - using iterator = const Char*; - - constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} - - /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) noexcept - : data_(s), size_(count) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - FMT_CONSTEXPR_CHAR_TRAITS - FMT_INLINE - basic_string_view(const Char* s) - : data_(s), - size_(detail::const_check(std::is_same::value && - !detail::is_constant_evaluated(true)) - ? std::strlen(reinterpret_cast(s)) - : 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) noexcept - : data_(s.data()), size_(s.size()) {} - - template >::value)> - FMT_CONSTEXPR basic_string_view(S s) noexcept - : data_(s.data()), size_(s.size()) {} - - /** Returns a pointer to the string data. */ - constexpr auto data() const noexcept -> const Char* { return data_; } - - /** Returns the string size. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - constexpr auto begin() const noexcept -> iterator { return data_; } - constexpr auto end() const noexcept -> iterator { return data_ + size_; } - - constexpr auto operator[](size_t pos) const noexcept -> const Char& { - return data_[pos]; - } - - FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { - data_ += n; - size_ -= n; - } - - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with( - basic_string_view sv) const noexcept -> bool { - return size_ >= sv.size_ && - std::char_traits::compare(data_, sv.data_, sv.size_) == 0; - } - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(Char c) const noexcept -> bool { - return size_ >= 1 && std::char_traits::eq(*data_, c); - } - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(const Char* s) const -> bool { - return starts_with(basic_string_view(s)); - } - - // Lexicographically compare this string reference to other. - FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { - 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; - } - - FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, - basic_string_view rhs) - -> bool { - return lhs.compare(rhs) == 0; - } - friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) != 0; - } - friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) < 0; - } - friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) <= 0; - } - friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) > 0; - } - friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) >= 0; - } -}; - -FMT_EXPORT -using string_view = basic_string_view; - -/** Specifies if ``T`` is a character type. Can be specialized by users. */ -FMT_EXPORT -template struct is_char : std::false_type {}; -template <> struct is_char : std::true_type {}; - -namespace detail { - -// A base class for compile-time strings. -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - -template ::value)> -FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { - return s; -} -template -inline auto to_string_view(const std::basic_string& s) - -> basic_string_view { - return s; -} -template -constexpr auto to_string_view(basic_string_view s) - -> basic_string_view { - return s; -} -template >::value)> -inline auto to_string_view(std_string_view s) -> basic_string_view { - return s; -} -template ::value)> -constexpr auto to_string_view(const S& s) - -> basic_string_view { - return basic_string_view(s); -} -void 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. -// ADL is intentionally disabled as to_string_view is not an extension point. -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::value_type; -}; - -enum class type { - none_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(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_opt, int128_type); -FMT_TYPE_CONSTANT(uint128_opt, 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); - -constexpr auto is_integral_type(type t) -> bool { - return t > type::none_type && t <= type::last_integer_type; -} -constexpr auto is_arithmetic_type(type t) -> bool { - return t > type::none_type && t <= type::last_numeric_type; -} - -constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } -constexpr auto in(type t, int set) -> bool { - return ((set >> static_cast(t)) & 1) != 0; -} - -// Bitsets of types. -enum { - sint_set = - set(type::int_type) | set(type::long_long_type) | set(type::int128_type), - uint_set = set(type::uint_type) | set(type::ulong_long_type) | - set(type::uint128_type), - bool_set = set(type::bool_type), - char_set = set(type::char_type), - float_set = set(type::float_type) | set(type::double_type) | - set(type::long_double_type), - string_set = set(type::string_type), - cstring_set = set(type::cstring_type), - pointer_set = set(type::pointer_type) -}; - -// DEPRECATED! -FMT_NORETURN FMT_API void throw_format_error(const char* message); - -struct error_handler { - constexpr error_handler() = default; - - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN void on_error(const char* message) { - throw_format_error(message); - } -}; -} // namespace detail - -/** Throws ``format_error`` with a given message. */ -using detail::throw_format_error; - -/** String's character type. */ -template using char_t = typename detail::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 the ``format_parse_context`` type alias for ``char`` instead. - \endrst - */ -FMT_EXPORT -template class basic_format_parse_context { - private: - basic_string_view format_str_; - int next_arg_id_; - - FMT_CONSTEXPR void do_check_arg_id(int id); - - public: - using char_type = Char; - using iterator = const Char*; - - explicit constexpr basic_format_parse_context( - basic_string_view format_str, int next_arg_id = 0) - : format_str_(format_str), next_arg_id_(next_arg_id) {} - - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ - constexpr auto begin() const noexcept -> iterator { - return format_str_.begin(); - } - - /** - Returns an iterator past the end of the format string range being parsed. - */ - constexpr auto end() const noexcept -> iterator { return format_str_.end(); } - - /** Advances the begin iterator to ``it``. */ - FMT_CONSTEXPR void advance_to(iterator it) { - format_str_.remove_prefix(detail::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 auto next_arg_id() -> int { - if (next_arg_id_ < 0) { - detail::throw_format_error( - "cannot switch from manual to automatic argument indexing"); - return 0; - } - int id = next_arg_id_++; - do_check_arg_id(id); - return id; - } - - /** - Reports an error if using the automatic argument indexing; otherwise - switches to the manual indexing. - */ - FMT_CONSTEXPR void check_arg_id(int id) { - if (next_arg_id_ > 0) { - detail::throw_format_error( - "cannot switch from automatic to manual argument indexing"); - return; - } - next_arg_id_ = -1; - do_check_arg_id(id); - } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - FMT_CONSTEXPR void check_dynamic_spec(int arg_id); -}; - -FMT_EXPORT -using format_parse_context = basic_format_parse_context; - -namespace detail { -// A parse context with extra data used only in compile-time checks. -template -class compile_parse_context : public basic_format_parse_context { - private: - int num_args_; - const type* types_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, int num_args, const type* types, - int next_arg_id = 0) - : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} - - constexpr auto num_args() const -> int { return num_args_; } - constexpr auto arg_type(int id) const -> type { return types_[id]; } - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) throw_format_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) throw_format_error("argument not found"); - } - using base::check_arg_id; - - FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { - detail::ignore_unused(arg_id); -#if !defined(__LCC__) - if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) - throw_format_error("width/precision is not integer"); -#endif - } -}; - -// Extracts a reference to the container from back_insert_iterator. -template -inline auto get_container(std::back_insert_iterator it) - -> Container& { - using base = std::back_insert_iterator; - struct accessor : base { - accessor(base b) : base(b) {} - using base::container; - }; - return *accessor(it).container; -} - -template -FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) - -> OutputIt { - while (begin != end) *out++ = static_cast(*begin++); - return out; -} - -template , U>::value&& is_char::value)> -FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { - if (is_constant_evaluated()) return copy_str(begin, end, out); - auto size = to_unsigned(end - begin); - if (size > 0) memcpy(out, begin, size * sizeof(U)); - return out + size; -} - -/** - \rst - A contiguous memory buffer with an optional growing ability. It is an internal - class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. - \endrst - */ -template class buffer { - private: - T* ptr_; - size_t size_; - size_t capacity_; - - protected: - // Don't initialize ptr_ since it is not accessed to save a few cycles. - FMT_MSC_WARNING(suppress : 26495) - FMT_CONSTEXPR buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept - : ptr_(p), size_(sz), capacity_(cap) {} - - FMT_CONSTEXPR20 ~buffer() = default; - buffer(buffer&&) = default; - - /** Sets the buffer data and capacity. */ - FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { - ptr_ = buf_data; - capacity_ = buf_capacity; - } - - /** Increases the buffer capacity to hold at least *capacity* elements. */ - // DEPRECATED! - virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; - - public: - using value_type = T; - using const_reference = const T&; - - buffer(const buffer&) = delete; - void operator=(const buffer&) = delete; - - FMT_INLINE auto begin() noexcept -> T* { return ptr_; } - FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } - - FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } - FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } - - /** Returns the size of this buffer. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - /** Returns the capacity of this buffer. */ - constexpr auto capacity() const noexcept -> size_t { return capacity_; } - - /** Returns a pointer to the buffer data (not null-terminated). */ - FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } - FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } - - /** Clears this buffer. */ - void clear() { size_ = 0; } - - // Tries resizing the buffer to contain *count* elements. If T is a POD type - // the new elements may not be initialized. - FMT_CONSTEXPR20 void try_resize(size_t count) { - try_reserve(count); - size_ = count <= capacity_ ? count : capacity_; - } - - // Tries increasing the buffer capacity to *new_capacity*. It can increase the - // capacity by a smaller amount than requested but guarantees there is space - // for at least one additional element either by increasing the capacity or by - // flushing the buffer if it is full. - FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); - } - - FMT_CONSTEXPR20 void push_back(const T& value) { - try_reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); - - template FMT_CONSTEXPR auto operator[](Idx index) -> T& { - return ptr_[index]; - } - template - FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { - return ptr_[index]; - } -}; - -struct buffer_traits { - explicit buffer_traits(size_t) {} - auto count() const -> size_t { return 0; } - auto limit(size_t size) -> size_t { return size; } -}; - -class fixed_buffer_traits { - private: - size_t count_ = 0; - size_t limit_; - - public: - explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} - auto count() const -> size_t { return count_; } - auto limit(size_t size) -> size_t { - size_t n = limit_ > count_ ? limit_ - count_ : 0; - count_ += size; - return size < n ? size : n; - } -}; - -// A buffer that writes to an output iterator when flushed. -template -class iterator_buffer final : public Traits, public buffer { - private: - OutputIt out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == buffer_size) flush(); - } - - void flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); - } - - public: - explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} - ~iterator_buffer() { flush(); } - - auto out() -> OutputIt { - flush(); - return out_; - } - auto count() const -> size_t { return Traits::count() + this->size(); } -}; - -template -class iterator_buffer final - : public fixed_buffer_traits, - public buffer { - private: - T* out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == this->capacity()) flush(); - } - - void flush() { - size_t n = this->limit(this->size()); - if (this->data() == out_) { - out_ += n; - this->set(data_, buffer_size); - } - this->clear(); - } - - public: - explicit iterator_buffer(T* out, size_t n = buffer_size) - : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : fixed_buffer_traits(other), - buffer(std::move(other)), - out_(other.out_) { - if (this->data() != out_) { - this->set(data_, buffer_size); - this->clear(); - } - } - ~iterator_buffer() { flush(); } - - auto out() -> T* { - flush(); - return out_; - } - auto count() const -> size_t { - return fixed_buffer_traits::count() + this->size(); - } -}; - -template class iterator_buffer final : public buffer { - protected: - FMT_CONSTEXPR20 void grow(size_t) override {} - - public: - explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} - - auto out() -> T* { return &*this->end(); } -}; - -// A buffer that writes to a container with the contiguous storage. -template -class iterator_buffer, - enable_if_t::value, - typename Container::value_type>> - final : public buffer { - private: - Container& container_; - - protected: - FMT_CONSTEXPR20 void grow(size_t capacity) override { - container_.resize(capacity); - this->set(&container_[0], capacity); - } - - public: - explicit iterator_buffer(Container& c) - : buffer(c.size()), container_(c) {} - explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) - : iterator_buffer(get_container(out)) {} - - auto out() -> std::back_insert_iterator { - return std::back_inserter(container_); - } -}; - -// A buffer that counts the number of code units written discarding the output. -template class counting_buffer final : public buffer { - private: - enum { buffer_size = 256 }; - T data_[buffer_size]; - size_t count_ = 0; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() != buffer_size) return; - count_ += this->size(); - this->clear(); - } - - public: - counting_buffer() : buffer(data_, 0, buffer_size) {} - - auto count() -> size_t { return count_ + this->size(); } -}; -} // namespace detail - -template -FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { - // Argument id is only checked at compile-time during parsing because - // formatting has its own validation. - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - if (id >= static_cast(this)->num_args()) - detail::throw_format_error("argument not found"); - } -} - -template -FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( - int arg_id) { - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - static_cast(this)->check_dynamic_spec(arg_id); - } -} - -FMT_EXPORT template class basic_format_arg; -FMT_EXPORT template class basic_format_args; -FMT_EXPORT template class dynamic_format_arg_store; - -// A formatter for objects of type T. -FMT_EXPORT -template -struct formatter { - // A deleted default constructor indicates a disabled formatter. - formatter() = delete; -}; - -// 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>; - -// An output iterator that appends to a buffer. -// It is used to reduce symbol sizes for the common case. -class appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; - - public: - using std::back_insert_iterator>::back_insert_iterator; - appender(base it) noexcept : base(it) {} - FMT_UNCHECKED_ITERATOR(appender); - - auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } -}; - -namespace detail { - -template -constexpr auto has_const_formatter_impl(T*) - -> decltype(typename Context::template formatter_type().format( - std::declval(), std::declval()), - true) { - return true; -} -template -constexpr auto has_const_formatter_impl(...) -> bool { - return false; -} -template -constexpr auto has_const_formatter() -> bool { - return has_const_formatter_impl(static_cast(nullptr)); -} - -template -using buffer_appender = conditional_t::value, appender, - std::back_insert_iterator>>; - -// Maps an output iterator to a buffer. -template -auto get_buffer(OutputIt out) -> iterator_buffer { - return iterator_buffer(out); -} -template , Buf>::value)> -auto get_buffer(std::back_insert_iterator out) -> buffer& { - return get_container(out); -} - -template -FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { - return buf.out(); -} -template -auto get_iterator(buffer&, OutputIt out) -> OutputIt { - return out; -} - -struct view {}; - -template struct named_arg : view { - const Char* name; - const T& value; - named_arg(const Char* n, const T& v) : name(n), value(v) {} -}; - -template struct named_arg_info { - const Char* name; - int id; -}; - -template -struct arg_data { - // args_[0].named_args points to named_args_ to avoid bloating format_args. - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; - named_arg_info named_args_[NUM_NAMED_ARGS]; - - template - arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} - arg_data(const arg_data& other) = delete; - auto args() const -> const T* { return args_ + 1; } - auto named_args() -> named_arg_info* { return named_args_; } -}; - -template -struct arg_data { - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; - - template - FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } - FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { - return nullptr; - } -}; - -template -inline void init_named_args(named_arg_info*, int, int) {} - -template struct is_named_arg : std::false_type {}; -template struct is_statically_named_arg : std::false_type {}; - -template -struct is_named_arg> : std::true_type {}; - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T&, const Tail&... args) { - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T& arg, const Tail&... args) { - named_args[named_arg_count++] = {arg.name, arg_count}; - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template -FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, - const Args&...) {} - -template constexpr auto count() -> size_t { return B ? 1 : 0; } -template constexpr auto count() -> size_t { - return (B1 ? 1 : 0) + count(); -} - -template constexpr auto count_named_args() -> size_t { - return count::value...>(); -} - -template -constexpr auto count_statically_named_args() -> size_t { - return count::value...>(); -} - -struct unformattable {}; -struct unformattable_char : unformattable {}; -struct unformattable_pointer : unformattable {}; - -template struct string_value { - const Char* data; - size_t size; -}; - -template struct named_arg_value { - const named_arg_info* data; - size_t size; -}; - -template struct custom_value { - using parse_context = typename Context::parse_context_type; - void* value; - void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); -}; - -// A formatting argument value. -template class value { - public: - using char_type = typename Context::char_type; - - union { - monostate no_value; - int int_value; - unsigned uint_value; - long long long_long_value; - unsigned long long ulong_long_value; - int128_opt int128_value; - uint128_opt 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; - named_arg_value named_args; - }; - - constexpr FMT_INLINE value() : no_value() {} - constexpr FMT_INLINE value(int val) : int_value(val) {} - constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} - constexpr FMT_INLINE value(long long val) : long_long_value(val) {} - constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_opt val) : int128_value(val) {} - FMT_INLINE value(uint128_opt val) : uint128_value(val) {} - constexpr FMT_INLINE value(float val) : float_value(val) {} - constexpr FMT_INLINE value(double val) : double_value(val) {} - FMT_INLINE value(long double val) : long_double_value(val) {} - constexpr FMT_INLINE value(bool val) : bool_value(val) {} - constexpr FMT_INLINE value(char_type val) : char_value(val) {} - FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { - string.data = val; - if (is_constant_evaluated()) string.size = {}; - } - FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { - string.data = val.data(); - string.size = val.size(); - } - FMT_INLINE value(const void* val) : pointer(val) {} - FMT_INLINE value(const named_arg_info* args, size_t size) - : named_args{args, size} {} - - template FMT_CONSTEXPR20 FMT_INLINE value(T& val) { - using value_type = remove_const_t; - custom.value = const_cast(std::addressof(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< - value_type, typename Context::template formatter_type>; - } - value(unformattable); - value(unformattable_char); - value(unformattable_pointer); - - private: - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg(void* arg, - typename Context::parse_context_type& parse_ctx, - Context& ctx) { - auto f = Formatter(); - parse_ctx.advance_to(f.parse(parse_ctx)); - using qualified_type = - conditional_t(), const T, T>; - // Calling format through a mutable reference is deprecated. - ctx.advance_to(f.format(*static_cast(arg), ctx)); - } -}; - -// 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; - -template struct format_as_result { - template ::value || std::is_class::value)> - static auto map(U*) -> remove_cvref_t()))>; - static auto map(...) -> void; - - using type = decltype(map(static_cast(nullptr))); -}; -template using format_as_t = typename format_as_result::type; - -template -struct has_format_as - : bool_constant, void>::value> {}; - -// Maps formatting arguments to core types. -// arg_mapper reports errors by returning unformattable instead of using -// static_assert because it's used in the is_formattable trait. -template struct arg_mapper { - using char_type = typename Context::char_type; - - FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) - -> unsigned long long { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } - - template ::value || - std::is_same::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { - return val; - } - template ::value || -#ifdef __cpp_char8_t - std::is_same::value || -#endif - std::is_same::value || - std::is_same::value) && - !std::is_same::value, - int> = 0> - FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { - return val; - } - - FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { - return val; - } - template ::value && !std::is_pointer::value && - std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { - return to_string_view(val); - } - template ::value && !std::is_pointer::value && - !std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { - return val; - } - - // Use SFINAE instead of a const T* parameter to avoid a conflict with the - // array overload. - template < - typename T, - FMT_ENABLE_IF( - std::is_pointer::value || std::is_member_pointer::value || - std::is_function::type>::value || - (std::is_array::value && - !std::is_convertible::value))> - FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { - return {}; - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { - return values; - } - - // Only map owning types because mapping views can be unsafe. - template , - FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(FMT_DECLTYPE_THIS map(U())) { - return map(format_as(val)); - } - - template > - struct formattable : bool_constant() || - (has_formatter::value && - !std::is_const::value)> {}; - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& { - return val; - } - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable { - return {}; - } - - template , - FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || - std::is_union::value) && - !is_string::value && !is_char::value && - !is_named_arg::value && - !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) - -> decltype(FMT_DECLTYPE_THIS do_map(val)) { - return do_map(val); - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) { - return map(named_arg.value); - } - - auto map(...) -> unformattable { return {}; } -}; - -// 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 = 4 }; -// Maximum number of arguments with packed types. -enum { max_packed_args = 62 / packed_arg_bits }; -enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; -enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; - -template -auto copy_str(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} -template -auto copy_str(InputIt begin, InputIt end, - std::back_insert_iterator out) - -> std::back_insert_iterator { - get_container(out).append(begin, end); - return out; -} - -template -FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { - return detail::copy_str(rng.begin(), rng.end(), out); -} - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { - using type = void; -}; -template using void_t = typename void_t_impl::type; -#else -template using void_t = void; -#endif - -template -struct is_output_iterator : std::false_type {}; - -template -struct is_output_iterator< - It, T, - void_t::iterator_category, - decltype(*std::declval() = std::declval())>> - : std::true_type {}; - -template struct is_back_insert_iterator : std::false_type {}; -template -struct is_back_insert_iterator> - : std::true_type {}; - -// A type-erased reference to an std::locale to avoid a heavy include. -class locale_ref { - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} - template explicit locale_ref(const Locale& loc); - - explicit operator bool() const noexcept { return locale_ != nullptr; } - - template auto get() const -> Locale; -}; - -template constexpr auto encode_types() -> unsigned long long { - return 0; -} - -template -constexpr auto encode_types() -> unsigned long long { - return static_cast(mapped_type_constant::value) | - (encode_types() << packed_arg_bits); -} - -#if defined(__cpp_if_constexpr) -// This type is intentionally undefined, only used for errors -template struct type_is_unformattable_for; -#endif - -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value { - using arg_type = remove_cvref_t().map(val))>; - - constexpr bool formattable_char = - !std::is_same::value; - static_assert(formattable_char, "Mixing character types is disallowed."); - - // Formatting of arbitrary pointers is disallowed. If you want to format a - // pointer cast it to `void*` or `const void*`. In particular, this forbids - // formatting of `[const] volatile char*` printed as bool by iostreams. - constexpr bool formattable_pointer = - !std::is_same::value; - static_assert(formattable_pointer, - "Formatting of non-void pointers is disallowed."); - - constexpr bool formattable = !std::is_same::value; -#if defined(__cpp_if_constexpr) - if constexpr (!formattable) { - type_is_unformattable_for _; - } -#endif - static_assert( - formattable, - "Cannot format an argument. To make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return {arg_mapper().map(val)}; -} - -template -FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg { - auto arg = basic_format_arg(); - arg.type_ = mapped_type_constant::value; - arg.value_ = make_arg(val); - return arg; -} - -template -FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { - return make_arg(val); -} -} // namespace detail -FMT_BEGIN_EXPORT - -// A formatting argument. Context is a template parameter for the compiled API -// where output can be unbuffered. -template class basic_format_arg { - private: - detail::value value_; - detail::type type_; - - template - friend FMT_CONSTEXPR auto detail::make_arg(T& value) - -> basic_format_arg; - - 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 dynamic_format_arg_store; - - using char_type = typename Context::char_type; - - template - friend struct detail::arg_data; - - basic_format_arg(const detail::named_arg_info* args, size_t size) - : value_(args, size) {} - - public: - class handle { - public: - explicit handle(detail::custom_value custom) : custom_(custom) {} - - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { - custom_.format(custom_.value, parse_ctx, ctx); - } - - private: - detail::custom_value custom_; - }; - - constexpr basic_format_arg() : type_(detail::type::none_type) {} - - constexpr explicit operator bool() const noexcept { - return type_ != detail::type::none_type; - } - - auto type() const -> detail::type { return type_; } - - auto is_integral() const -> bool { return detail::is_integral_type(type_); } - auto is_arithmetic() const -> bool { - return detail::is_arithmetic_type(type_); - } - - FMT_INLINE auto format_custom(const char_type* parse_begin, - typename Context::parse_context_type& parse_ctx, - Context& ctx) -> bool { - if (type_ != detail::type::custom_type) return false; - parse_ctx.advance_to(parse_begin); - value_.custom.format(value_.custom.value, parse_ctx, ctx); - return true; - } -}; - -/** - \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 - */ -// DEPRECATED! -template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); -} - -// Formatting context. -template class basic_format_context { - private: - OutputIt out_; - basic_format_args args_; - detail::locale_ref loc_; - - public: - using iterator = OutputIt; - using format_arg = basic_format_arg; - using format_args = basic_format_args; - using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; - - /** The character type for the output. */ - using char_type = Char; - - basic_format_context(basic_format_context&&) = default; - 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. - */ - constexpr basic_format_context(OutputIt out, format_args ctx_args, - detail::locale_ref loc = {}) - : out_(out), args_(ctx_args), loc_(loc) {} - - constexpr auto arg(int id) const -> format_arg { return args_.get(id); } - FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { - return args_.get(name); - } - FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { - return args_.get_id(name); - } - auto args() const -> const format_args& { return args_; } - - // DEPRECATED! - FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } - - // Returns an iterator to the beginning of the output range. - FMT_CONSTEXPR auto out() -> iterator { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { - if (!detail::is_back_insert_iterator()) out_ = it; - } - - FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } -}; - -template -using buffer_context = - basic_format_context, Char>; -using format_context = buffer_context; - -template -using is_formattable = bool_constant>() - .map(std::declval()))>::value>; - -/** - \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 -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - static const size_t num_args = sizeof...(Args); - static constexpr size_t num_named_args = detail::count_named_args(); - static const bool is_packed = num_args <= detail::max_packed_args; - - using value_type = conditional_t, - basic_format_arg>; - - detail::arg_data - data_; - - friend class basic_format_args; - - static constexpr unsigned long long desc = - (is_packed ? detail::encode_types() - : detail::is_unpacked_bit | num_args) | - (num_named_args != 0 - ? static_cast(detail::has_named_args_bit) - : 0); - - public: - template - FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) - : -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - basic_format_args(*this), -#endif - data_{detail::make_arg(args)...} { - if (detail::const_check(num_named_args != 0)) - detail::init_named_args(data_.named_args(), 0, 0, args...); - } -}; - -/** - \rst - Constructs a `~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::format_context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ -// Arguments are taken by lvalue references to avoid some lifetime issues. -template -constexpr auto make_format_args(T&... args) - -> format_arg_store...> { - return {args...}; -} - -/** - \rst - Returns a named argument to be used in a formatting function. - It should only be used in a call to a formatting function or - `dynamic_format_arg_store::push_back`. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst - */ -template -inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); - return {name, arg}; -} -FMT_END_EXPORT - -/** - \rst - A view of a collection of formatting arguments. To avoid lifetime issues it - should only be used as a parameter type in type-erased functions such as - ``vformat``:: - - void vlog(string_view format_str, format_args args); // OK - format_args args = make_format_args(); // Error: dangling reference - \endrst - */ -template class basic_format_args { - public: - using size_type = int; - using format_arg = basic_format_arg; - - private: - // A descriptor that contains information about formatting arguments. - // If the number of arguments is less or equal to max_packed_args then - // argument types are passed in the descriptor. This reduces binary code size - // per formatting function call. - unsigned long long desc_; - union { - // If is_packed() returns true then argument values are stored in values_; - // otherwise they are stored in args_. This is done to improve cache - // locality and reduce compiled code size since 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 detail::value* values_; - const format_arg* args_; - }; - - constexpr auto is_packed() const -> bool { - return (desc_ & detail::is_unpacked_bit) == 0; - } - auto has_named_args() const -> bool { - return (desc_ & detail::has_named_args_bit) != 0; - } - - FMT_CONSTEXPR auto type(int index) const -> detail::type { - int shift = index * detail::packed_arg_bits; - unsigned int mask = (1 << detail::packed_arg_bits) - 1; - return static_cast((desc_ >> shift) & mask); - } - - constexpr FMT_INLINE basic_format_args(unsigned long long desc, - const detail::value* values) - : desc_(desc), values_(values) {} - constexpr basic_format_args(unsigned long long desc, const format_arg* args) - : desc_(desc), args_(args) {} - - public: - constexpr basic_format_args() : desc_(0), args_(nullptr) {} - - /** - \rst - Constructs a `basic_format_args` object from `~fmt::format_arg_store`. - \endrst - */ - template - constexpr FMT_INLINE basic_format_args( - const format_arg_store& store) - : basic_format_args(format_arg_store::desc, - store.data_.args()) {} - - /** - \rst - Constructs a `basic_format_args` object from - `~fmt::dynamic_format_arg_store`. - \endrst - */ - constexpr FMT_INLINE basic_format_args( - const dynamic_format_arg_store& store) - : basic_format_args(store.get_types(), store.data()) {} - - /** - \rst - Constructs a `basic_format_args` object from a dynamic set of arguments. - \endrst - */ - constexpr basic_format_args(const format_arg* args, int count) - : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), - args) {} - - /** Returns the argument with the specified id. */ - FMT_CONSTEXPR auto get(int id) const -> format_arg { - format_arg arg; - if (!is_packed()) { - if (id < max_size()) arg = args_[id]; - return arg; - } - if (id >= detail::max_packed_args) return arg; - arg.type_ = type(id); - if (arg.type_ == detail::type::none_type) return arg; - arg.value_ = values_[id]; - return arg; - } - - template - auto get(basic_string_view name) const -> format_arg { - int id = get_id(name); - return id >= 0 ? get(id) : format_arg(); - } - - template - auto get_id(basic_string_view name) const -> int { - if (!has_named_args()) return -1; - const auto& named_args = - (is_packed() ? values_[-1] : args_[-1].value_).named_args; - for (size_t i = 0; i < named_args.size; ++i) { - if (named_args.data[i].name == name) return named_args.data[i].id; - } - return -1; - } - - auto max_size() const -> int { - unsigned long long max_packed = detail::max_packed_args; - return static_cast(is_packed() ? max_packed - : desc_ & ~detail::is_unpacked_bit); - } -}; - -/** An alias to ``basic_format_args``. */ -// A separate type would result in shorter symbols but break ABI compatibility -// between clang and gcc on ARM (#1919). -FMT_EXPORT using format_args = basic_format_args; - -// We cannot use enum classes as bit fields because of a gcc bug, so we put them -// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). -// Additionally, if an underlying type is specified, older gcc incorrectly warns -// that the type is too small. Both bugs are fixed in gcc 9.3. -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 -# define FMT_ENUM_UNDERLYING_TYPE(type) -#else -# define FMT_ENUM_UNDERLYING_TYPE(type) : type -#endif -namespace align { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, - numeric}; -} -using align_t = align::type; -namespace sign { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; -} -using sign_t = sign::type; - -namespace detail { - -// Workaround an array initialization issue in gcc 4.8. -template struct fill_t { - private: - enum { max_size = 4 }; - Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; - unsigned char size_ = 1; - - public: - FMT_CONSTEXPR void operator=(basic_string_view s) { - auto size = s.size(); - FMT_ASSERT(size <= max_size, "invalid fill"); - for (size_t i = 0; i < size; ++i) data_[i] = s[i]; - size_ = static_cast(size); - } - - constexpr auto size() const -> size_t { return size_; } - constexpr auto data() const -> const Char* { return data_; } - - FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } - FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { - return data_[index]; - } -}; -} // namespace detail - -enum class presentation_type : unsigned char { - none, - dec, // 'd' - oct, // 'o' - hex_lower, // 'x' - hex_upper, // 'X' - bin_lower, // 'b' - bin_upper, // 'B' - hexfloat_lower, // 'a' - hexfloat_upper, // 'A' - exp_lower, // 'e' - exp_upper, // 'E' - fixed_lower, // 'f' - fixed_upper, // 'F' - general_lower, // 'g' - general_upper, // 'G' - chr, // 'c' - string, // 's' - pointer, // 'p' - debug // '?' -}; - -// Format specifiers for built-in and string types. -template struct format_specs { - int width; - int precision; - presentation_type type; - align_t align : 4; - sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). - bool localized : 1; - detail::fill_t fill; - - constexpr format_specs() - : width(0), - precision(-1), - type(presentation_type::none), - align(align::none), - sign(sign::none), - alt(false), - localized(false) {} -}; - -namespace detail { - -enum class arg_id_kind { none, index, name }; - -// An argument reference. -template struct arg_ref { - FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} - - FMT_CONSTEXPR explicit arg_ref(int index) - : kind(arg_id_kind::index), val(index) {} - FMT_CONSTEXPR explicit arg_ref(basic_string_view name) - : kind(arg_id_kind::name), val(name) {} - - FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { - kind = arg_id_kind::index; - val.index = idx; - return *this; - } - - arg_id_kind kind; - union value { - FMT_CONSTEXPR value(int idx = 0) : index(idx) {} - FMT_CONSTEXPR value(basic_string_view n) : name(n) {} - - int index; - basic_string_view name; - } val; -}; - -// Format specifiers with width and precision resolved at formatting rather -// than parsing time to allow reusing the same parsed specifiers with -// different sets of arguments (precompilation of format strings). -template -struct dynamic_format_specs : format_specs { - arg_ref width_ref; - arg_ref precision_ref; -}; - -// Converts a character to ASCII. Returns '\0' on conversion failure. -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} - -// Returns the number of code units in a code point or 1 on error. -template -FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { - if (const_check(sizeof(Char) != 1)) return 1; - auto c = static_cast(*begin); - return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; -} - -// Return the result via the out param to workaround gcc bug 77539. -template -FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { - for (out = first; out != last; ++out) { - if (*out == value) return true; - } - return false; -} - -template <> -inline auto find(const char* first, const char* last, char value, - const char*& out) -> bool { - out = static_cast( - std::memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - -// 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 auto parse_nonnegative_int(const Char*& begin, const Char* end, - int error_value) noexcept -> int { - FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0, prev = 0; - auto p = begin; - do { - prev = value; - value = value * 10 + unsigned(*p - '0'); - ++p; - } while (p != end && '0' <= *p && *p <= '9'); - auto num_digits = p - begin; - begin = p; - if (num_digits <= std::numeric_limits::digits10) - return static_cast(value); - // Check for overflow. - const unsigned max = to_unsigned((std::numeric_limits::max)()); - return num_digits == std::numeric_limits::digits10 + 1 && - prev * 10ull + unsigned(p[-1] - '0') <= max - ? static_cast(value) - : error_value; -} - -FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { - switch (c) { - case '<': - return align::left; - case '>': - return align::right; - case '^': - return align::center; - } - return align::none; -} - -template constexpr auto is_name_start(Char c) -> bool { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; -} - -template -FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - Char c = *begin; - if (c >= '0' && c <= '9') { - int index = 0; - constexpr int max = (std::numeric_limits::max)(); - if (c != '0') - index = parse_nonnegative_int(begin, end, max); - else - ++begin; - if (begin == end || (*begin != '}' && *begin != ':')) - throw_format_error("invalid format string"); - else - handler.on_index(index); - return begin; - } - if (!is_name_start(c)) { - throw_format_error("invalid format string"); - return begin; - } - auto it = begin; - do { - ++it; - } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); - handler.on_name({begin, to_unsigned(it - begin)}); - return it; -} - -template -FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); - Char c = *begin; - if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); - handler.on_auto(); - return begin; -} - -template struct dynamic_spec_id_handler { - basic_format_parse_context& ctx; - arg_ref& ref; - - FMT_CONSTEXPR void on_auto() { - int id = ctx.next_arg_id(); - ref = arg_ref(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_index(int id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_name(basic_string_view id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - } -}; - -// Parses [integer | "{" [arg_id] "}"]. -template -FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - FMT_ASSERT(begin != end, ""); - if ('0' <= *begin && *begin <= '9') { - int val = parse_nonnegative_int(begin, end, -1); - if (val != -1) - value = val; - else - throw_format_error("number is too big"); - } else if (*begin == '{') { - ++begin; - auto handler = dynamic_spec_id_handler{ctx, ref}; - if (begin != end) begin = parse_arg_id(begin, end, handler); - if (begin != end && *begin == '}') return ++begin; - throw_format_error("invalid format string"); - } - return begin; -} - -template -FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - ++begin; - if (begin == end || *begin == '}') { - throw_format_error("invalid precision"); - return begin; - } - return parse_dynamic_spec(begin, end, value, ref, ctx); -} - -enum class state { start, align, sign, hash, zero, width, precision, locale }; - -// Parses standard format specifiers. -template -FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( - const Char* begin, const Char* end, dynamic_format_specs& specs, - basic_format_parse_context& ctx, type arg_type) -> const Char* { - auto c = '\0'; - if (end - begin > 1) { - auto next = to_ascii(begin[1]); - c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; - } else { - if (begin == end) return begin; - c = to_ascii(*begin); - } - - struct { - state current_state = state::start; - FMT_CONSTEXPR void operator()(state s, bool valid = true) { - if (current_state >= s || !valid) - throw_format_error("invalid format specifier"); - current_state = s; - } - } enter_state; - - using pres = presentation_type; - constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; - struct { - const Char*& begin; - dynamic_format_specs& specs; - type arg_type; - - FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { - if (!in(arg_type, set)) { - if (arg_type == type::none_type) return begin; - throw_format_error("invalid format specifier"); - } - specs.type = pres_type; - return begin + 1; - } - } parse_presentation_type{begin, specs, arg_type}; - - for (;;) { - switch (c) { - case '<': - case '>': - case '^': - enter_state(state::align); - specs.align = parse_align(c); - ++begin; - break; - case '+': - case '-': - case ' ': - if (arg_type == type::none_type) return begin; - enter_state(state::sign, in(arg_type, sint_set | float_set)); - switch (c) { - case '+': - specs.sign = sign::plus; - break; - case '-': - specs.sign = sign::minus; - break; - case ' ': - specs.sign = sign::space; - break; - } - ++begin; - break; - case '#': - if (arg_type == type::none_type) return begin; - enter_state(state::hash, is_arithmetic_type(arg_type)); - specs.alt = true; - ++begin; - break; - case '0': - enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) { - if (arg_type == type::none_type) return begin; - throw_format_error("format specifier requires numeric argument"); - } - if (specs.align == align::none) { - // Ignore 0 if align is specified for compatibility with std::format. - specs.align = align::numeric; - specs.fill[0] = Char('0'); - } - ++begin; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '{': - enter_state(state::width); - begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); - break; - case '.': - if (arg_type == type::none_type) return begin; - enter_state(state::precision, - in(arg_type, float_set | string_set | cstring_set)); - begin = parse_precision(begin, end, specs.precision, specs.precision_ref, - ctx); - break; - case 'L': - if (arg_type == type::none_type) return begin; - enter_state(state::locale, is_arithmetic_type(arg_type)); - specs.localized = true; - ++begin; - break; - case 'd': - return parse_presentation_type(pres::dec, integral_set); - case 'o': - return parse_presentation_type(pres::oct, integral_set); - case 'x': - return parse_presentation_type(pres::hex_lower, integral_set); - case 'X': - return parse_presentation_type(pres::hex_upper, integral_set); - case 'b': - return parse_presentation_type(pres::bin_lower, integral_set); - case 'B': - return parse_presentation_type(pres::bin_upper, integral_set); - case 'a': - return parse_presentation_type(pres::hexfloat_lower, float_set); - case 'A': - return parse_presentation_type(pres::hexfloat_upper, float_set); - case 'e': - return parse_presentation_type(pres::exp_lower, float_set); - case 'E': - return parse_presentation_type(pres::exp_upper, float_set); - case 'f': - return parse_presentation_type(pres::fixed_lower, float_set); - case 'F': - return parse_presentation_type(pres::fixed_upper, float_set); - case 'g': - return parse_presentation_type(pres::general_lower, float_set); - case 'G': - return parse_presentation_type(pres::general_upper, float_set); - case 'c': - if (arg_type == type::bool_type) - throw_format_error("invalid format specifier"); - return parse_presentation_type(pres::chr, integral_set); - case 's': - return parse_presentation_type(pres::string, - bool_set | string_set | cstring_set); - case 'p': - return parse_presentation_type(pres::pointer, pointer_set | cstring_set); - case '?': - return parse_presentation_type(pres::debug, - char_set | string_set | cstring_set); - case '}': - return begin; - default: { - if (*begin == '}') return begin; - // Parse fill and alignment. - auto fill_end = begin + code_point_length(begin); - if (end - fill_end <= 0) { - throw_format_error("invalid format specifier"); - return begin; - } - if (*begin == '{') { - throw_format_error("invalid fill character '{'"); - return begin; - } - auto align = parse_align(to_ascii(*fill_end)); - enter_state(state::align, align != align::none); - specs.fill = {begin, to_unsigned(fill_end - begin)}; - specs.align = align; - begin = fill_end + 1; - } - } - if (begin == end) return begin; - c = to_ascii(*begin); - } -} - -template -FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - struct id_adapter { - Handler& handler; - int arg_id; - - FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void on_name(basic_string_view id) { - arg_id = handler.on_arg_id(id); - } - }; - - ++begin; - if (begin == end) return handler.on_error("invalid format string"), end; - if (*begin == '}') { - handler.on_replacement_field(handler.on_arg_id(), begin); - } else if (*begin == '{') { - handler.on_text(begin, begin + 1); - } else { - auto adapter = id_adapter{handler, 0}; - begin = parse_arg_id(begin, end, adapter); - Char c = begin != end ? *begin : Char(); - if (c == '}') { - handler.on_replacement_field(adapter.arg_id, begin); - } else if (c == ':') { - begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); - if (begin == end || *begin != '}') - return handler.on_error("unknown format specifier"), end; - } else { - return handler.on_error("missing '}' in format string"), end; - } - } - return begin + 1; -} - -template -FMT_CONSTEXPR FMT_INLINE void parse_format_string( - basic_string_view format_str, Handler&& handler) { - auto begin = format_str.data(); - auto end = begin + format_str.size(); - if (end - begin < 32) { - // Use a simple loop instead of memchr for small strings. - const Char* p = begin; - while (p != end) { - auto c = *p++; - if (c == '{') { - handler.on_text(begin, p - 1); - begin = p = parse_replacement_field(p - 1, end, handler); - } else if (c == '}') { - if (p == end || *p != '}') - return handler.on_error("unmatched '}' in format string"); - handler.on_text(begin, p); - begin = ++p; - } - } - handler.on_text(begin, end); - return; - } - struct writer { - FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { - if (from == to) return; - for (;;) { - const Char* p = nullptr; - if (!find(from, to, Char('}'), p)) - return handler_.on_text(from, to); - ++p; - if (p == to || *p != '}') - return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(from, p); - from = p + 1; - } - } - Handler& handler_; - } write = {handler}; - while (begin != end) { - // Doing two passes with memchr (one for '{' and another for '}') is up to - // 2.5x faster than the naive one-pass implementation on big format strings. - const Char* p = begin; - if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) - return write(begin, end); - write(begin, p); - begin = parse_replacement_field(p, end, handler); - } -} - -template ::value> struct strip_named_arg { - using type = T; -}; -template struct strip_named_arg { - using type = remove_cvref_t; -}; - -template -FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) - -> decltype(ctx.begin()) { - using char_type = typename ParseContext::char_type; - using context = buffer_context; - using mapped_type = conditional_t< - mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), - typename strip_named_arg::type>; -#if defined(__cpp_if_constexpr) - if constexpr (std::is_default_constructible< - formatter>::value) { - return formatter().parse(ctx); - } else { - type_is_unformattable_for _; - return ctx.begin(); - } -#else - return formatter().parse(ctx); -#endif -} - -// Checks char specs and returns true iff the presentation type is char-like. -template -FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { - if (specs.type != presentation_type::none && - specs.type != presentation_type::chr && - specs.type != presentation_type::debug) { - return false; - } - if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - throw_format_error("invalid format specifier for char"); - return true; -} - -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -template -constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (is_statically_named_arg()) { - if (name == T::name) return N; - } - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name(name); - (void)name; // Workaround an MSVC bug about "unused" parameter. - return -1; -} -#endif - -template -FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name<0, Args...>(name); -#endif - (void)name; - return -1; -} - -template class format_string_checker { - private: - using parse_context_type = compile_parse_context; - static constexpr int num_args = sizeof...(Args); - - // Format specifier parsing function. - // In the future basic_format_parse_context will replace compile_parse_context - // here and will use is_constant_evaluated and downcasting to access the data - // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. - using parse_func = const Char* (*)(parse_context_type&); - - type types_[num_args > 0 ? static_cast(num_args) : 1]; - parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; - - public: - explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) - : types_{mapped_type_constant>::value...}, - context_(fmt, num_args, types_), - parse_funcs_{&parse_format_specs...} {} - - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - - FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return context_.check_arg_id(id), id; - } - FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - auto index = get_arg_index_by_name(id); - if (index < 0) on_error("named argument is not found"); - return index; -#else - (void)id; - on_error("compile-time checks for named arguments require C++20 support"); - return 0; -#endif - } - - FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { - on_format_specs(id, begin, begin); // Call parse() on empty specs. - } - - FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) - -> const Char* { - context_.advance_to(begin); - // id >= 0 check is a workaround for gcc 10 bug (#2065). - return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; - } - - FMT_CONSTEXPR void on_error(const char* message) { - throw_format_error(message); - } -}; - -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif -} -template ::value)> -void check_format_string(S format_str) { - using char_t = typename S::char_type; - FMT_CONSTEXPR auto s = basic_string_view(format_str); - using checker = format_string_checker...>; - FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); - ignore_unused(error); -} - -template struct vformat_args { - using type = basic_format_args< - basic_format_context>, Char>>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -// Use vformat_args and avoid type_identity to keep symbols short. -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}); - -FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); -#ifndef _WIN32 -inline void vprint_mojibake(std::FILE*, string_view, format_args) {} -#endif -} // namespace detail - -FMT_BEGIN_EXPORT - -// A formatter specialization for natively supported types. -template -struct formatter::value != - detail::type::custom_type>> { - private: - detail::dynamic_format_specs specs_; - - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - auto type = detail::type_constant::value; - auto end = - detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); - if (type == detail::type::char_type) detail::check_char_specs(specs_); - return end; - } - - template ::value, - FMT_ENABLE_IF(U == detail::type::string_type || - U == detail::type::cstring_type || - U == detail::type::char_type)> - FMT_CONSTEXPR void set_debug_format(bool set = true) { - specs_.type = set ? presentation_type::debug : presentation_type::none; - } - - template - FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const - -> decltype(ctx.out()); -}; - -template struct runtime_format_string { - basic_string_view str; -}; - -/** A compile-time format string. */ -template class basic_format_string { - private: - basic_string_view str_; - - public: - template >::value)> - FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { - static_assert( - detail::count< - (std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); -#ifdef FMT_HAS_CONSTEVAL - if constexpr (detail::count_named_args() == - detail::count_statically_named_args()) { - using checker = - detail::format_string_checker...>; - detail::parse_format_string(str_, checker(s)); - } -#else - detail::check_format_string(s); -#endif - } - basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} - - FMT_INLINE operator basic_string_view() const { return str_; } - FMT_INLINE auto get() const -> basic_string_view { return str_; } -}; - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -// Workaround broken conversion on older gcc. -template using format_string = string_view; -inline auto runtime(string_view s) -> string_view { return s; } -#else -template -using format_string = basic_format_string...>; -/** - \rst - Creates a runtime format string. - - **Example**:: - - // Check format string at runtime instead of compile-time. - fmt::print(fmt::runtime("{:d}"), "I am not a number"); - \endrst - */ -inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } -#endif - -FMT_API auto vformat(string_view fmt, format_args args) -> std::string; - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and returns the result - as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}.", 42); - \endrst -*/ -template -FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) - -> std::string { - return vformat(fmt, fmt::make_format_args(args...)); -} - -/** Formats a string and writes the output to ``out``. */ -template ::value)> -auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { - auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, fmt, args, {}); - return detail::get_iterator(buf, out); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes the result to - the output iterator ``out`` and returns the iterator past the end of the output - range. `format_to` does not append a terminating null character. - - **Example**:: - - auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); - \endrst - */ -template ::value)> -FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) - -> OutputIt { - return vformat_to(out, fmt, fmt::make_format_args(args...)); -} - -template struct format_to_n_result { - /** Iterator past the end of the output range. */ - OutputIt out; - /** Total (not truncated) output size. */ - size_t size; -}; - -template ::value)> -auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) - -> format_to_n_result { - using traits = detail::fixed_buffer_traits; - auto buf = detail::iterator_buffer(out, n); - detail::vformat_to(buf, fmt, args, {}); - return {buf.out(), buf.count()}; -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` - characters of the result to the output iterator ``out`` and returns the total - (not truncated) output size and the iterator past the end of the output range. - `format_to_n` does not append a terminating null character. - \endrst - */ -template ::value)> -FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, - T&&... args) -> format_to_n_result { - return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); -} - -/** Returns the number of chars in the output of ``format(fmt, args...)``. */ -template -FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, - T&&... args) -> size_t { - auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); - return buf.count(); -} - -FMT_API void vprint(string_view fmt, format_args args); -FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout``. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -template -FMT_INLINE void print(format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(fmt, vargs) - : detail::vprint_mojibake(stdout, fmt, vargs); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f``. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ -template -FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(f, fmt, vargs) - : detail::vprint_mojibake(f, fmt, vargs); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f`` followed by a newline. - */ -template -FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { - return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout`` followed by a newline. - */ -template -FMT_INLINE void println(format_string fmt, T&&... args) { - return fmt::println(stdout, fmt, std::forward(args)...); -} - -FMT_END_EXPORT -FMT_GCC_PRAGMA("GCC pop_options") -FMT_END_NAMESPACE - -#ifdef FMT_HEADER_ONLY -# include "format.h" -#endif -#endif // FMT_CORE_H_ +#include "format.h" diff --git a/deps/spdlog/include/spdlog/fmt/bundled/fmt.license.rst b/deps/spdlog/include/spdlog/fmt/bundled/fmt.license.rst index f0ec3db4d2a..1cd1ef92696 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/fmt.license.rst +++ b/deps/spdlog/include/spdlog/fmt/bundled/fmt.license.rst @@ -1,4 +1,4 @@ -Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/deps/spdlog/include/spdlog/fmt/bundled/format-inl.h b/deps/spdlog/include/spdlog/fmt/bundled/format-inl.h index efac5d1f88f..a1e01661178 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/format-inl.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/format-inl.h @@ -8,36 +8,36 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include -#include // errno -#include -#include -#include - -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -# include +#ifndef FMT_MODULE +# include +# include // errno +# include +# include +# include #endif -#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty #endif #include "format.h" +#if FMT_USE_LOCALE +# include +#endif + +#ifndef FMT_FUNC +# define FMT_FUNC +#endif + FMT_BEGIN_NAMESPACE namespace detail { FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when - // writing to stderr fails - std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); - // Chosen instead of std::abort to satisfy Clang in CUDA mode during device - // code pass. - std::terminate(); -} - -FMT_FUNC void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); + // writing to stderr fails. + fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + abort(); } FMT_FUNC void format_error_code(detail::buffer& out, int error_code, @@ -56,89 +56,105 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, ++error_code_size; } error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); - auto it = buffer_appender(out); + auto it = appender(out); if (message.size() <= inline_buffer_size - error_code_size) fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); FMT_ASSERT(out.size() <= inline_buffer_size, ""); } -FMT_FUNC void report_error(format_func func, int error_code, - const char* message) noexcept { +FMT_FUNC void do_report_error(format_func func, int error_code, + const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); - // Don't use fwrite_fully because the latter may throw. + // Don't use fwrite_all because the latter may throw. if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) std::fputc('\n', stderr); } // A wrapper around fwrite that throws on error. -inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) { +inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, 1, count, stream); if (written < count) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +#if FMT_USE_LOCALE +using std::locale; +using std::numpunct; +using std::use_facet; + template locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); } +#else +struct locale {}; +template struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template Facet use_facet(locale) { return {}; } +#endif // FMT_USE_LOCALE template auto locale_ref::get() const -> Locale { - static_assert(std::is_same::value, ""); - return locale_ ? *static_cast(locale_) : std::locale(); + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); } template FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { - auto& facet = std::use_facet>(loc.get()); + auto&& facet = use_facet>(loc.get()); auto grouping = facet.grouping(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } template FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { - return std::use_facet>(loc.get()) - .decimal_point(); + return use_facet>(loc.get()).decimal_point(); } -#else -template -FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { - return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; -} -template FMT_FUNC Char decimal_point_impl(locale_ref) { - return '.'; -} -#endif +#if FMT_USE_LOCALE FMT_FUNC auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + const format_specs& specs, locale_ref loc) -> bool { auto locale = loc.get(); // We cannot use the num_put facet because it may produce output in // a wrong encoding. using facet = format_facet; if (std::has_facet(locale)) - return std::use_facet(locale).put(out, value, specs); + return use_facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs); -#endif - return false; } +#endif } // namespace detail +FMT_FUNC void report_error(const char* message) { +#if FMT_USE_EXCEPTIONS + // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings + // from MSVC. + FMT_THROW(format_error(message)); +#else + fputs(message, stderr); + abort(); +#endif +} + template typename Locale::id format_facet::id; -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR template format_facet::format_facet(Locale& loc) { - auto& numpunct = std::use_facet>(loc); - grouping_ = numpunct.grouping(); - if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); + auto& np = detail::use_facet>(loc); + grouping_ = np.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); } +#if FMT_USE_LOCALE template <> FMT_API FMT_FUNC auto format_facet::do_put( - appender out, loc_value val, const format_specs<>& specs) const -> bool { + appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); } @@ -196,7 +212,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { return (e * 631305 - 261663) >> 21; } -FMT_INLINE_VARIABLE constexpr struct { +FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { uint32_t divisor; int shift_amount; } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; @@ -1081,7 +1097,7 @@ template <> struct cache_accessor { return {r.high(), r.low() == 0}; } - static auto compute_delta(cache_entry_type const& cache, int beta) noexcept + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } @@ -1411,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); - write(std::back_inserter(out), std::system_error(ec, message).what()); + detail::write(appender(out), std::system_error(ec, message).what()); return; } FMT_CATCH(...) {} @@ -1420,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, FMT_FUNC void report_system_error(int error_code, const char* message) noexcept { - report_error(format_system_error, error_code, message); + do_report_error(format_system_error, error_code, message); } FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { @@ -1432,9 +1448,251 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { } namespace detail { -#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) + +FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc) { + auto out = appender(buf); + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) + return args.get(0).visit(default_arg_formatter{out}); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} + +template struct span { + T* data; + size_t size; +}; + +template auto flockfile(F* f) -> decltype(_lock_file(f)) { + _lock_file(f); +} +template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { + _unlock_file(f); +} + +#ifndef getc_unlocked +template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { + return _fgetc_nolock(f); +} +#endif + +template +struct has_flockfile : std::false_type {}; + +template +struct has_flockfile()))>> + : std::true_type {}; + +// A FILE wrapper. F is FILE defined as a template parameter to make system API +// detection work. +template class file_base { + public: + F* file_; + + public: + file_base(F* file) : file_(file) {} + operator F*() const { return file_; } + + // Reads a code unit from the stream. + auto get() -> int { + int result = getc_unlocked(file_); + if (result == EOF && ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); + return result; + } + + // Puts the code unit back into the stream buffer. + void unget(char c) { + if (ungetc(c, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); + } + + void flush() { fflush(this->file_); } +}; + +// A FILE wrapper for glibc. +template class glibc_file : public file_base { + private: + enum { + line_buffered = 0x200, // _IO_LINE_BUF + unbuffered = 2 // _IO_UNBUFFERED + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_IO_write_ptr; + } + + // Returns the file's read buffer. + auto get_read_buffer() const -> span { + auto ptr = this->file_->_IO_read_ptr; + return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; + } + + // Returns the file's write buffer. + auto get_write_buffer() const -> span { + auto ptr = this->file_->_IO_write_ptr; + return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; + } + + void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + char* end = this->file_->_IO_write_end; + return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); + } + + void flush() { fflush_unlocked(this->file_); } +}; + +// A FILE wrapper for Apple's libc. +template class apple_file : public file_base { + private: + enum { + line_buffered = 1, // __SNBF + unbuffered = 2 // __SLBF + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_p) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_p; + ++this->file_->_w; + } + + auto get_read_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_r)}; + } + + auto get_write_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_bf._base + this->file_->_bf._size - + this->file_->_p)}; + } + + void advance_write_buffer(size_t size) { + this->file_->_p += size; + this->file_->_w -= size; + } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + return memchr(this->file_->_p + this->file_->_w, '\n', + to_unsigned(-this->file_->_w)); + } +}; + +// A fallback FILE wrapper. +template class fallback_file : public file_base { + private: + char next_; // The next unconsumed character in the buffer. + bool has_next_ = false; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { return false; } + auto needs_flush() const -> bool { return false; } + void init_buffer() {} + + auto get_read_buffer() const -> span { + return {&next_, has_next_ ? 1u : 0u}; + } + + auto get_write_buffer() const -> span { return {nullptr, 0}; } + + void advance_write_buffer(size_t) {} + + auto get() -> int { + has_next_ = false; + return file_base::get(); + } + + void unget(char c) { + file_base::unget(c); + next_ = c; + has_next_ = true; + } +}; + +#ifndef FMT_USE_FALLBACK_FILE +# define FMT_USE_FALLBACK_FILE 0 +#endif + +template +auto get_file(F* f, int) -> apple_file { + return f; +} +template +inline auto get_file(F* f, int) -> glibc_file { + return f; +} + +inline auto get_file(FILE* f, ...) -> fallback_file { return f; } + +using file_ref = decltype(get_file(static_cast(nullptr), 0)); + +template +class file_print_buffer : public buffer { + public: + explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} +}; + +template +class file_print_buffer::value>> + : public buffer { + private: + file_ref file_; + + static void grow(buffer& base, size_t) { + auto& self = static_cast(base); + self.file_.advance_write_buffer(self.size()); + if (self.file_.get_write_buffer().size == 0) self.file_.flush(); + auto buf = self.file_.get_write_buffer(); + FMT_ASSERT(buf.size > 0, ""); + self.set(buf.data, buf.size); + self.clear(); + } + + public: + explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { + flockfile(f); + file_.init_buffer(); + auto buf = file_.get_write_buffer(); + set(buf.data, buf.size); + } + ~file_print_buffer() { + file_.advance_write_buffer(size()); + bool flush = file_.needs_flush(); + F* f = file_; // Make funlockfile depend on the template parameter F + funlockfile(f); // for the system API detection to work. + if (flush) fflush(file_); + } +}; + +#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) FMT_FUNC auto write_console(int, string_view) -> bool { return false; } -FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // @@ -1445,36 +1703,48 @@ FMT_FUNC bool write_console(int fd, string_view text) { return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), nullptr, nullptr) != 0; } - -FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool { - return write_console(_fileno(f), text); -} #endif #ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. -FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, + bool newline) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); - fwrite_fully(buffer.data(), buffer.size(), f); + if (newline) buffer.push_back('\n'); + fwrite_all(buffer.data(), buffer.size(), f); } #endif FMT_FUNC void print(std::FILE* f, string_view text) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) int fd = _fileno(f); if (_isatty(fd)) { std::fflush(f); if (write_console(fd, text)) return; } #endif - fwrite_fully(text.data(), text.size(), f); + fwrite_all(text.data(), text.size(), f); } } // namespace detail +FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) + return vprint_buffered(f, fmt, args); + auto&& buffer = detail::file_print_buffer<>(f); + return detail::vformat_to(buffer, fmt, args); +} + +FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); + buffer.push_back('\n'); detail::print(f, {buffer.data(), buffer.size()}); } diff --git a/deps/spdlog/include/spdlog/fmt/bundled/format.h b/deps/spdlog/include/spdlog/fmt/bundled/format.h index 7637c8a0d06..50e571442e5 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/format.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/format.h @@ -33,20 +33,59 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include // std::signbit -#include // uint32_t -#include // std::memcpy -#include // std::initializer_list -#include // std::numeric_limits -#include // std::uninitialized_copy -#include // std::runtime_error -#include // std::system_error - -#ifdef __cpp_lib_bit_cast -# include // std::bit_cast +#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define FMT_REMOVE_TRANSITIVE_INCLUDES #endif -#include "core.h" +#include "base.h" + +#ifndef FMT_MODULE +# include // std::signbit +# include // std::byte +# include // uint32_t +# include // std::memcpy +# include // std::numeric_limits +# include // std::bad_alloc +# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) +// Workaround for pre gcc 5 libstdc++. +# include // std::allocator_traits +# endif +# include // std::runtime_error +# include // std::string +# include // std::system_error + +// Check FMT_CPLUSPLUS to avoid a warning in MSVC. +# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L +# include // std::bit_cast +# endif + +// libc++ supports string_view in pre-c++17. +# if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include +# define FMT_USE_STRING_VIEW +# endif + +# if FMT_MSC_VERSION +# include // _BitScanReverse[64], _umul128 +# endif +#endif // FMT_MODULE + +#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) +// Use the provided definition. +#elif defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif defined(__cpp_nontype_template_args) && \ + __cpp_nontype_template_args >= 201911L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#endif #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline @@ -54,43 +93,15 @@ # define FMT_INLINE_VARIABLE #endif -#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -# define FMT_FALLTHROUGH [[fallthrough]] -#elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -#elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] +// Check if RTTI is disabled. +#ifdef FMT_USE_RTTI +// Use the provided definition. +#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ + defined(__INTEL_RTTI__) || defined(__RTTI) +// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. +# define FMT_USE_RTTI 1 #else -# define FMT_FALLTHROUGH -#endif - -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VERSION -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - -#ifndef FMT_NO_UNIQUE_ADDRESS -# if FMT_CPLUSPLUS >= 202002L -# if FMT_HAS_CPP_ATTRIBUTE(no_unique_address) -# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485) -# elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION -# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -# endif -# endif -#endif -#ifndef FMT_NO_UNIQUE_ADDRESS -# define FMT_NO_UNIQUE_ADDRESS +# define FMT_USE_RTTI 0 #endif // Visibility when compiled as a shared library/object. @@ -100,20 +111,26 @@ # define FMT_SO_VISIBILITY(value) #endif -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_NOINLINE __attribute__((noinline)) #else # define FMT_NOINLINE #endif +// GCC 4.9 doesn't support qualified names in specializations. +namespace std { +template struct iterator_traits> { + using iterator_category = output_iterator_tag; + using value_type = T; + using difference_type = + decltype(static_cast(nullptr) - static_cast(nullptr)); + using pointer = void; + using reference = void; +}; +} // namespace std + #ifndef FMT_THROW -# if FMT_EXCEPTIONS +# if FMT_USE_EXCEPTIONS # if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { @@ -132,38 +149,8 @@ FMT_END_NAMESPACE # else # define FMT_THROW(x) \ ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) -# 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 - -#ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif -#endif - -#ifndef FMT_USE_USER_DEFINED_LITERALS -// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. -// -// GCC before 4.9 requires a space in `operator"" _a` which is invalid in later -// compiler versions. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 409 || \ - FMT_MSC_VERSION >= 1900) && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) -# define FMT_USE_USER_DEFINED_LITERALS 1 -# else -# define FMT_USE_USER_DEFINED_LITERALS 0 -# endif -#endif +# endif // FMT_USE_EXCEPTIONS +#endif // FMT_THROW // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the @@ -173,7 +160,15 @@ FMT_END_NAMESPACE # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif -// __builtin_clz is broken in clang with Microsoft CodeGen: +FMT_BEGIN_NAMESPACE + +template +struct is_contiguous> + : std::true_type {}; + +namespace detail { + +// __builtin_clz is broken in clang with Microsoft codegen: // https://github.com/fmtlib/fmt/issues/519. #if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION @@ -184,53 +179,30 @@ FMT_END_NAMESPACE # endif #endif -// __builtin_ctz is broken in Intel Compiler Classic on Windows: -// https://github.com/fmtlib/fmt/issues/2510. -#ifndef __ICL -# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) -# endif -# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) -# endif -#endif - -#if FMT_MSC_VERSION -# include // _BitScanReverse[64], _BitScanForward[64], _umul128 -#endif - -// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// Some compilers masquerade as both MSVC and GCC but 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_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ - !defined(FMT_BUILTIN_CTZLL) -FMT_BEGIN_NAMESPACE -namespace detail { +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# if !defined(__clang__) -# pragma intrinsic(_BitScanForward) +# ifndef __clang__ # pragma intrinsic(_BitScanReverse) -# if defined(_WIN64) -# pragma intrinsic(_BitScanForward64) +# ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif # endif inline auto clz(uint32_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. 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. - FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) inline auto clzll(uint64_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); @@ -241,43 +213,10 @@ inline auto clzll(uint64_t x) -> int { // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) - -inline auto ctz(uint32_t x) -> int { - unsigned long r = 0; - _BitScanForward(&r, x); - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - return static_cast(r); -} -# define FMT_BUILTIN_CTZ(n) detail::ctz(n) - -inline auto ctzll(uint64_t x) -> int { - unsigned long r = 0; - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -# ifdef _WIN64 - _BitScanForward64(&r, x); -# else - // Scan the low 32 bits. - if (_BitScanForward(&r, static_cast(x))) return static_cast(r); - // Scan the high 32 bits. - _BitScanForward(&r, static_cast(x >> 32)); - r += 32; -# endif - return static_cast(r); -} -# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) -} // namespace detail -FMT_END_NAMESPACE -#endif - -FMT_BEGIN_NAMESPACE -namespace detail { +#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { ignore_unused(condition); @@ -286,16 +225,23 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { #endif } -template struct string_literal { - static constexpr CharT value[sizeof...(C)] = {C...}; - constexpr operator basic_string_view() const { +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#else +template struct std_string_view { + operator basic_string_view() const; +}; +#endif + +template struct string_literal { + static constexpr Char value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { return {value, sizeof...(C)}; } }; - #if FMT_CPLUSPLUS < 201703L -template -constexpr CharT string_literal::value[sizeof...(C)]; +template +constexpr Char string_literal::value[sizeof...(C)]; #endif // Implementation of std::bit_cast for pre-C++20. @@ -367,13 +313,14 @@ class uint128_fallback { -> uint128_fallback { return {~n.hi_, ~n.lo_}; } - friend auto operator+(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { + friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { auto result = uint128_fallback(lhs); result += rhs; return result; } - friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs) -> uint128_fallback { FMT_ASSERT(lhs.hi_ == 0, ""); uint64_t hi = (lhs.lo_ >> 32) * rhs; @@ -381,7 +328,7 @@ class uint128_fallback { uint64_t new_lo = (hi << 32) + lo; return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; } - friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs) -> uint128_fallback { return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; } @@ -454,23 +401,24 @@ template constexpr auto num_bits() -> int { } // std::numeric_limits::digits may return 0 for 128-bit ints. template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t // and 128-bit pointers to uint128_fallback. template sizeof(From))> inline auto bit_cast(const From& from) -> To { - constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); struct data_t { - unsigned value[static_cast(size)]; + unsigned short value[static_cast(size)]; } data = bit_cast(from); auto result = To(); if (const_check(is_big_endian())) { for (int i = 0; i < size; ++i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } else { for (int i = size - 1; i >= 0; --i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } return result; } @@ -506,38 +454,25 @@ FMT_INLINE void assume(bool condition) { #endif } -// An approximation of iterator_t for pre-C++20 systems. -template -using iterator_t = decltype(std::begin(std::declval())); -template using sentinel_t = decltype(std::end(std::declval())); - -// A workaround for std::string not having mutable data() until C++17. -template -inline auto get_data(std::basic_string& s) -> Char* { - return &s[0]; -} -template -inline auto get_data(Container& c) -> typename Container::value_type* { - return c.data(); -} - // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to it. -template ::value)> +template ::value&& + is_contiguous::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline auto -reserve(std::back_insert_iterator it, size_t n) -> - typename Container::value_type* { - Container& c = get_container(it); +FMT_CONSTEXPR20 inline auto +reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { + auto& c = get_container(it); size_t size = c.size(); c.resize(size + n); - return get_data(c) + size; + return &c[size]; } template -inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { +FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) + -> basic_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; @@ -556,18 +491,22 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template auto to_pointer(buffer_appender it, size_t n) -> T* { +template +FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; buf.try_resize(size + n); return buf.data() + size; } -template ::value)> -inline auto base_iterator(std::back_insert_iterator it, - typename Container::value_type*) - -> std::back_insert_iterator { +template ::value&& + is_contiguous::value)> +inline auto base_iterator(OutputIt it, + typename OutputIt::container_type::value_type*) + -> OutputIt { return it; } @@ -586,23 +525,15 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) } template FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { - if (is_constant_evaluated()) { - return fill_n(out, count, value); - } + if (is_constant_evaluated()) return fill_n(out, count, value); std::memset(out, value, to_unsigned(count)); return out + count; } -#ifdef __cpp_char8_t -using char8_type = char8_t; -#else -enum char8_type : unsigned char {}; -#endif - template -FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, - OutputIt out) -> OutputIt { - return copy_str(begin, end, out); +FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: @@ -673,6 +604,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); return result ? (error ? buf_ptr + 1 : end) : nullptr; }; + auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. if (s.size() >= block_size) { @@ -681,17 +613,20 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { if (!p) return; } } - if (auto num_chars_left = s.data() + s.size() - p) { - char buf[2 * block_size - 1] = {}; - copy_str(p, p + num_chars_left, buf); - const char* buf_ptr = buf; - do { - auto end = decode(buf_ptr, p); - if (!end) return; - p += end - buf_ptr; - buf_ptr = end; - } while (buf_ptr - buf < num_chars_left); - } + auto num_chars_left = to_unsigned(s.data() + s.size() - p); + if (num_chars_left == 0) return; + + // Suppress bogus -Wstringop-overflow. + if (FMT_GCC_VERSION) num_chars_left &= 3; + char buf[2 * block_size - 1] = {}; + copy(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr < buf + num_chars_left); } template @@ -706,7 +641,7 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { struct count_code_points { size_t* count; FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { - *count += detail::to_unsigned( + *count += to_unsigned( 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants @@ -734,15 +669,9 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { return num_code_points; } -inline auto compute_width(basic_string_view s) -> size_t { - return compute_width( - string_view(reinterpret_cast(s.data()), s.size())); -} - template inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - size_t size = s.size(); - return n < size ? n : size; + return min_of(n, s.size()); } // Calculates the index of the nth code point in a UTF-8 string. @@ -760,12 +689,6 @@ inline auto code_point_index(string_view s, size_t n) -> size_t { return result; } -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - return code_point_index( - string_view(reinterpret_cast(s.data()), s.size()), n); -} - template struct is_integral : std::is_integral {}; template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; @@ -781,45 +704,28 @@ using is_integer = !std::is_same::value && !std::is_same::value>; -#ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 -#endif -#ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 -#endif -#ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 -#endif - -#ifndef FMT_USE_FLOAT128 -# ifdef __clang__ -// Clang emulates GCC, so it has to appear early. -# if FMT_HAS_INCLUDE() -# define FMT_USE_FLOAT128 1 -# endif -# elif defined(__GNUC__) -// GNU C++: -# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) -# define FMT_USE_FLOAT128 1 -# endif -# endif -# ifndef FMT_USE_FLOAT128 -# define FMT_USE_FLOAT128 0 -# endif +#if defined(FMT_USE_FLOAT128) +// Use the provided definition. +#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ + !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +#else +# define FMT_USE_FLOAT128 0 #endif - #if FMT_USE_FLOAT128 using float128 = __float128; #else -using float128 = void; +struct float128 {}; #endif + template using is_float128 = std::is_same; -template -using is_floating_point = - bool_constant::value || is_float128::value>; +template struct is_floating_point : std::is_floating_point {}; +template <> struct is_floating_point : std::true_type {}; -template ::value> +template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; @@ -831,24 +737,21 @@ using is_double_double = bool_constant::digits == 106>; # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif -template -template -void buffer::append(const U* begin, const U* end) { - while (begin != end) { - auto count = to_unsigned(end - begin); - try_reserve(size_ + count); - auto free_cap = capacity_ - size_; - if (free_cap < count) count = free_cap; - std::uninitialized_copy_n(begin, count, ptr_ + size_); - size_ += count; - begin += count; +// An allocator that uses malloc/free to allow removing dependency on the C++ +// standard libary runtime. +template struct allocator { + using value_type = T; + + T* allocate(size_t n) { + FMT_ASSERT(n <= max_value() / sizeof(T), ""); + T* p = static_cast(malloc(n * sizeof(T))); + if (!p) FMT_THROW(std::bad_alloc()); + return p; } -} -template -struct is_locale : std::false_type {}; -template -struct is_locale> : std::true_type {}; + void deallocate(T* p, size_t) { free(p); } +}; + } // namespace detail FMT_BEGIN_EXPORT @@ -858,29 +761,21 @@ FMT_BEGIN_EXPORT 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 the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - - auto out = fmt::memory_buffer(); - fmt::format_to(std::back_inserter(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 + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. */ template > -class basic_memory_buffer final : public detail::buffer { + typename Allocator = detail::allocator> +class basic_memory_buffer : public detail::buffer { private: T store_[SIZE]; @@ -893,37 +788,37 @@ class basic_memory_buffer final : public detail::buffer { if (data != store_) alloc_.deallocate(data, this->capacity()); } - protected: - FMT_CONSTEXPR20 void grow(size_t size) override { + static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { detail::abort_fuzzing_if(size > 5000); - const size_t max_size = std::allocator_traits::max_size(alloc_); - size_t old_capacity = this->capacity(); + auto& self = static_cast(buf); + const size_t max_size = + std::allocator_traits::max_size(self.alloc_); + size_t old_capacity = buf.capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; else if (new_capacity > max_size) - new_capacity = size > max_size ? size : max_size; - T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); + new_capacity = max_of(size, max_size); + T* old_data = buf.data(); + T* new_data = self.alloc_.allocate(new_capacity); // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). - detail::assume(this->size() <= new_capacity); + detail::assume(buf.size() <= new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy_n(old_data, this->size(), new_data); - this->set(new_data, new_capacity); + memcpy(new_data, old_data, buf.size() * sizeof(T)); + self.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_) alloc_.deallocate(old_data, old_capacity); + if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); } public: using value_type = T; using const_reference = const T&; - FMT_CONSTEXPR20 explicit basic_memory_buffer( + FMT_CONSTEXPR explicit basic_memory_buffer( const Allocator& alloc = Allocator()) - : alloc_(alloc) { + : detail::buffer(grow), alloc_(alloc) { this->set(store_, SIZE); if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } @@ -937,7 +832,7 @@ class basic_memory_buffer final : public detail::buffer { size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - detail::copy_str(other.store_, other.store_ + size, store_); + detail::copy(other.store_, other.store_ + size, store_); } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called @@ -949,21 +844,14 @@ class basic_memory_buffer final : public detail::buffer { } public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept + : detail::buffer(grow) { move(other); } - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ + /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); @@ -974,120 +862,108 @@ class basic_memory_buffer final : public detail::buffer { // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } - /** - Resizes the buffer to contain *count* elements. If T is a POD type new - elements may not be initialized. - */ - FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + /// Resizes the buffer to contain `count` elements. If T is a POD type new + /// elements may not be initialized. + FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } - /** Increases the buffer capacity to *new_capacity*. */ + /// Increases the buffer capacity to `new_capacity`. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } using detail::buffer::append; template - void append(const ContiguousRange& range) { + FMT_CONSTEXPR20 void append(const ContiguousRange& range) { append(range.data(), range.data() + range.size()); } }; using memory_buffer = basic_memory_buffer; -template -struct is_contiguous> : std::true_type { +template +FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) + -> std::string { + auto size = buf.size(); + detail::assume(size < std::string().max_size()); + return {buf.data(), size}; +} + +// A writer to a buffered stream. It doesn't own the underlying stream. +class writer { + private: + detail::buffer* buf_; + + // We cannot create a file buffer in advance because any write to a FILE may + // invalidate it. + FILE* file_; + + public: + inline writer(FILE* f) : buf_(nullptr), file_(f) {} + inline writer(detail::buffer& buf) : buf_(&buf) {} + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + if (buf_) + fmt::format_to(appender(*buf_), fmt, std::forward(args)...); + else + fmt::print(file_, fmt, std::forward(args)...); + } }; -FMT_END_EXPORT -namespace detail { -FMT_API auto write_console(int fd, string_view text) -> bool; -FMT_API auto write_console(std::FILE* f, string_view text) -> bool; -FMT_API void print(std::FILE*, string_view); -} // namespace detail +class string_buffer { + private: + std::string str_; + detail::container_buffer buf_; -FMT_BEGIN_EXPORT + public: + inline string_buffer() : buf_(str_) {} + + inline operator writer() { return buf_; } + inline std::string& str() { return str_; } +}; + +template +struct is_contiguous> : std::true_type { +}; // Suppress a misleading warning in older versions of clang. -#if FMT_CLANG_VERSION -# pragma clang diagnostic ignored "-Wweak-vtables" -#endif +FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") -/** An error reported from a formatting function. */ +/// An error reported from a formatting function. class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; -namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS +class loc_value; + +FMT_END_EXPORT +namespace detail { +FMT_API auto write_console(int fd, string_view text) -> bool; +FMT_API void print(FILE*, string_view); +} // namespace detail + +namespace detail { template struct fixed_string { - constexpr fixed_string(const Char (&str)[N]) { - detail::copy_str(static_cast(str), - str + N, data); + FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) { + detail::copy(static_cast(s), s + N, + data); } Char data[N] = {}; }; -#endif // Converts a compile-time string to basic_string_view. -template +FMT_EXPORT template constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view { // Remove trailing NUL character if needed. Won't be present if this is used // with a raw character array (i.e. not defined as a string). return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; } -template -constexpr auto compile_string_to_view(detail::std_string_view s) +FMT_EXPORT template +constexpr auto compile_string_to_view(basic_string_view s) -> basic_string_view { - return {s.data(), s.size()}; + return s; } -} // namespace detail_exported - -class loc_value { - private: - basic_format_arg value_; - - public: - template ::value)> - loc_value(T value) : value_(detail::make_arg(value)) {} - - template ::value)> - loc_value(T) {} - - template auto visit(Visitor&& vis) -> decltype(vis(0)) { - return visit_format_arg(vis, value_); - } -}; - -// A locale facet that formats values in UTF-8. -// It is parameterized on the locale to avoid the heavy include. -template class format_facet : public Locale::facet { - private: - std::string separator_; - std::string grouping_; - std::string decimal_point_; - - protected: - virtual auto do_put(appender out, loc_value val, - const format_specs<>& specs) const -> bool; - - public: - static FMT_API typename Locale::id id; - - explicit format_facet(Locale& loc); - explicit format_facet(string_view sep = "", - std::initializer_list g = {3}, - std::string decimal_point = ".") - : separator_(sep.data(), sep.size()), - grouping_(g.begin(), g.end()), - decimal_point_(decimal_point) {} - - auto put(appender out, loc_value val, const format_specs<>& specs) const - -> bool { - return do_put(out, val, specs); - } -}; - -namespace detail { // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. @@ -1100,14 +976,6 @@ constexpr auto is_negative(T) -> bool { return false; } -template -FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { - if (std::is_same()) return FMT_USE_FLOAT; - if (std::is_same()) return FMT_USE_DOUBLE; - if (std::is_same()) return FMT_USE_LONG_DOUBLE; - return true; -} - // Smallest of uint32_t, uint64_t, uint128_t that is large enough to // represent all values of an integral type T. template @@ -1124,21 +992,22 @@ using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr auto digits2(size_t value) -> const char* { - // GCC generates slightly better code when value is pointer-size. - return &"0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"[value * 2]; -} - -// Sign is a template parameter to workaround a bug in gcc 4.8. -template constexpr auto sign(Sign s) -> Char { -#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 - static_assert(std::is_same::value, ""); -#endif - return static_cast("\0-+ "[s]); +// GCC generates slightly better code when value is pointer-size. +inline auto digits2(size_t value) -> const char* { + // Align data since unaligned access may be slower when crossing a + // hardware-specific boundary. + alignas(2) static const char data[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + return &data[value * 2]; +} + +template constexpr auto getsign(sign s) -> Char { + return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> + (static_cast(s) * 8)); } template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { @@ -1186,9 +1055,7 @@ inline auto do_count_digits(uint64_t n) -> int { // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1238,9 +1105,7 @@ FMT_INLINE auto do_count_digits(uint32_t n) -> int { // Optional version of count_digits for better performance on 32-bit platforms. FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1277,6 +1142,17 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } +#ifndef FMT_HEADER_ONLY +FMT_BEGIN_EXPORT +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +FMT_END_EXPORT +#endif // FMT_HEADER_ONLY + // Compares two characters for equality. template auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); @@ -1285,83 +1161,99 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } -// Copies two characters from src to dst. +// Writes a two-digit value to out. template -FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { - if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { - memcpy(dst, src, 2); +FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { + if (!is_constant_evaluated() && std::is_same::value && + !FMT_OPTIMIZE_SIZE) { + memcpy(out, digits2(value), 2); return; } - *dst++ = static_cast(*src++); - *dst = static_cast(*src); + *out++ = static_cast('0' + value / 10); + *out = static_cast('0' + value % 10); } -template struct format_decimal_result { - Iterator begin; - Iterator end; -}; - -// Formats a decimal unsigned integer value writing into out pointing to a -// buffer of specified size. The caller must ensure that the buffer is large -// enough. +// Formats a decimal unsigned integer value writing to out pointing to a buffer +// of specified size. The caller must ensure that the buffer is large enough. template -FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) - -> format_decimal_result { +FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) + -> Char* { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); - out += size; - Char* end = out; + unsigned n = to_unsigned(size); 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. - out -= 2; - copy2(out, digits2(static_cast(value % 100))); + n -= 2; + write2digits(out + n, static_cast(value % 100)); value /= 100; } - if (value < 10) { - *--out = static_cast('0' + value); - return {out, end}; + if (value >= 10) { + n -= 2; + write2digits(out + n, static_cast(value)); + } else { + out[--n] = static_cast('0' + value); } - out -= 2; - copy2(out, digits2(static_cast(value))); - return {out, end}; -} - -template >::value)> -FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) - -> format_decimal_result { - // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1] = {}; - auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str_noinline(buffer, end, out)}; + return out + n; } -template -FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) -> Char* { - buffer += num_digits; - Char* end = buffer; - do { - const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(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 +FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, + int num_digits) -> Char* { + do_format_decimal(out, value, num_digits); + return out + num_digits; } -template -FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits, - bool upper = false) -> It { +template >::value)> +FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + do_format_decimal(ptr, value, num_digits); + return out; + } + // Buffer is large enough to hold all digits (digits10 + 1). + char buffer[digits10() + 1]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + do_format_decimal(buffer, value, num_digits); + return copy_noinline(buffer, buffer + num_digits, out); +} + +template +FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, + int size, bool upper = false) -> Char* { + out += size; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= base_bits) != 0); + return out; +} + +// Formats an unsigned integer in the power of two base (binary, octal, hex). +template +FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, + int num_digits, bool upper = false) -> Char* { + do_format_base2e(base_bits, out, value, num_digits, upper); + return out + num_digits; +} + +template ::value)> +FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, + int num_digits, bool upper = false) + -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { - format_uint(ptr, value, num_digits, upper); + format_base2e(base_bits, ptr, value, num_digits, upper); return out; } - // 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 detail::copy_str_noinline(buffer, buffer + num_digits, out); + // Make buffer large enough for any base. + char buffer[num_bits()]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + format_base2e(base_bits, buffer, value, num_digits, upper); + return detail::copy_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. @@ -1371,10 +1263,12 @@ class utf8_to_utf16 { public: FMT_API explicit utf8_to_utf16(string_view s); - operator basic_string_view() const { return {&buffer_[0], size()}; } - auto size() const -> size_t { return buffer_.size() - 1; } - auto c_str() const -> const wchar_t* { return &buffer_[0]; } - auto str() const -> std::wstring { return {&buffer_[0], size()}; } + inline operator basic_string_view() const { + return {&buffer_[0], size()}; + } + inline auto size() const -> size_t { return buffer_.size() - 1; } + inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } + inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; enum class to_utf8_error_policy { abort, replace }; @@ -1421,10 +1315,12 @@ template class to_utf8 { if (policy == to_utf8_error_policy::abort) return false; buf.append(string_view("\xEF\xBF\xBD")); --p; + continue; } else { c = (c << 10) + static_cast(*p) - 0x35fdc00; } - } else if (c < 0x80) { + } + if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); @@ -1592,25 +1488,30 @@ template constexpr auto exponent_bias() -> int { } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template -FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { +template +FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { - *it++ = static_cast('-'); + *out++ = static_cast('-'); exp = -exp; } else { - *it++ = static_cast('+'); - } - if (exp >= 100) { - const char* top = digits2(to_unsigned(exp / 100)); - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; + *out++ = static_cast('+'); } - const char* d = digits2(to_unsigned(exp)); - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; + auto uexp = static_cast(exp); + if (is_constant_evaluated()) { + if (uexp < 10) *out++ = '0'; + return format_decimal(out, uexp, count_digits(uexp)); + } + if (uexp >= 100u) { + const char* top = digits2(uexp / 100); + if (uexp >= 1000u) *out++ = static_cast(top[0]); + *out++ = static_cast(top[1]); + uexp %= 100; + } + const char* d = digits2(uexp); + *out++ = static_cast(d[0]); + *out++ = static_cast(d[1]); + return out; } // A floating-point number f * pow(2, e) where F is an unsigned type. @@ -1711,67 +1612,69 @@ constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } -template -FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, - const fill_t& fill) -> OutputIt { - auto fill_size = fill.size(); - if (fill_size == 1) return detail::fill_n(it, n, fill[0]); - auto data = fill.data(); - for (size_t i = 0; i < n; ++i) - it = copy_str(data, data + fill_size, it); +template +FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, + const basic_specs& specs) -> OutputIt { + auto fill_size = specs.fill_size(); + if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); + if (const Char* data = specs.fill()) { + for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); + } return it; } // Writes the output of f, padded according to format specifications in specs. // size: output size in code units. // width: output display width in (terminal) column positions. -template -FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { - static_assert(align == align::left || align == align::right, ""); + static_assert(default_align == align::left || default_align == align::right, + ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; // Shifts are encoded as string literals because static constexpr is not // supported in constexpr functions. - auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; - size_t left_padding = padding >> shifts[specs.align]; + auto* shifts = + default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[static_cast(specs.align())]; size_t right_padding = padding - left_padding; - auto it = reserve(out, size + padding * specs.fill.size()); - if (left_padding != 0) it = fill(it, left_padding, specs.fill); + auto it = reserve(out, size + padding * specs.fill_size()); + if (left_padding != 0) it = fill(it, left_padding, specs); it = f(it); - if (right_padding != 0) it = fill(it, right_padding, specs.fill); + if (right_padding != 0) it = fill(it, right_padding, specs); return base_iterator(out, it); } -template -constexpr auto write_padded(OutputIt out, const format_specs& specs, +constexpr auto write_padded(OutputIt out, const format_specs& specs, size_t size, F&& f) -> OutputIt { - return write_padded(out, specs, size, size, f); + return write_padded(out, specs, size, size, f); } -template +template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, - const format_specs& specs) -> OutputIt { - return write_padded( + const format_specs& specs = {}) -> OutputIt { + return write_padded( out, specs, bytes.size(), [bytes](reserve_iterator it) { const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); + return copy(data, data + bytes.size(), it); }); } template -auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); + return format_base2e(4, it, value, num_digits); }; - return specs ? write_padded(out, *specs, size, write) + return specs ? write_padded(out, *specs, size, write) : base_iterator(out, write(reserve(out, size))); } @@ -1779,8 +1682,9 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) FMT_API auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { - return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || - !is_printable(cp); + if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; + if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false; + return !is_printable(cp); } template struct find_escape_result { @@ -1789,17 +1693,11 @@ template struct find_escape_result { uint32_t cp; }; -template -using make_unsigned_char = - typename conditional_t::value, - std::make_unsigned, - type_identity>::type; - template auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { - uint32_t cp = static_cast>(*begin); + uint32_t cp = static_cast>(*begin); if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } @@ -1808,7 +1706,7 @@ auto find_escape(const Char* begin, const Char* end) inline auto find_escape(const char* begin, const char* end) -> find_escape_result { - if (!is_utf8()) return find_escape(begin, end); + if (const_check(!use_utf8)) return find_escape(begin, end); auto result = find_escape_result{end, nullptr, 0}; for_each_codepoint(string_view(begin, to_unsigned(end - begin)), [&](uint32_t cp, string_view sv) { @@ -1821,40 +1719,14 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ - using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) - template auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { *out++ = static_cast('\\'); *out++ = static_cast(prefix); Char buf[width]; fill_n(buf, width, static_cast('0')); - format_uint<4>(buf, cp, width); - return copy_str(buf, buf + width, out); + format_base2e(4, buf, cp, width); + return copy(buf, buf + width, out); } template @@ -1874,23 +1746,15 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) *out++ = static_cast('\\'); c = static_cast('t'); break; - case '"': - FMT_FALLTHROUGH; - case '\'': - FMT_FALLTHROUGH; - case '\\': - *out++ = static_cast('\\'); - break; + case '"': FMT_FALLTHROUGH; + case '\'': FMT_FALLTHROUGH; + case '\\': *out++ = static_cast('\\'); break; default: - if (escape.cp < 0x100) { - return write_codepoint<2, Char>(out, 'x', escape.cp); - } - if (escape.cp < 0x10000) { + if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); + if (escape.cp < 0x10000) return write_codepoint<4, Char>(out, 'u', escape.cp); - } - if (escape.cp < 0x110000) { + if (escape.cp < 0x110000) return write_codepoint<8, Char>(out, 'U', escape.cp); - } for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { out = write_codepoint<2, Char>(out, 'x', @@ -1909,7 +1773,7 @@ auto write_escaped_string(OutputIt out, basic_string_view str) auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy(begin, escape.begin, out); begin = escape.end; if (!begin) break; out = write_escaped_cp(out, escape); @@ -1936,74 +1800,23 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt { template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, - const format_specs& specs) -> OutputIt { - bool is_debug = specs.type == presentation_type::debug; - return write_padded(out, specs, 1, [=](reserve_iterator it) { + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); } template -FMT_CONSTEXPR auto write(OutputIt out, Char value, - const format_specs& specs, locale_ref loc = {}) - -> OutputIt { +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { // char is formatted as unsigned char for consistency across platforms. using unsigned_type = conditional_t::value, unsigned char, unsigned>; return check_char_specs(specs) - ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); -} - -// Data for write_int that doesn't depend on output iterator type. It is used to -// avoid template code bloat. -template struct write_int_data { - size_t size; - size_t padding; - - FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, - const format_specs& specs) - : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { - if (specs.align == align::numeric) { - auto width = to_unsigned(specs.width); - if (width > size) { - padding = width - size; - size = width; - } - } else if (specs.precision > num_digits) { - size = (prefix >> 24) + to_unsigned(specs.precision); - padding = to_unsigned(specs.precision - num_digits); - } - } -}; - -// Writes an integer in the format -// -// where are written by write_digits(it). -// prefix contains chars in three lower bytes and the size in the fourth byte. -template -FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, - unsigned prefix, - const format_specs& specs, - W write_digits) -> OutputIt { - // Slightly faster check for specs.width == 0 && specs.precision == -1. - if ((specs.width | (specs.precision + 1)) == 0) { - auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); - if (prefix != 0) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - } - return base_iterator(out, write_digits(it)); - } - auto data = write_int_data(num_digits, prefix, specs); - return write_padded( - out, specs, data.size, [=](reserve_iterator it) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - it = detail::fill_n(it, data.padding, static_cast('0')); - return write_digits(it); - }); + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); } template class digit_grouping { @@ -2028,7 +1841,9 @@ template class digit_grouping { } public: - explicit digit_grouping(locale_ref loc, bool localized = true) { + template ::value)> + explicit digit_grouping(Locale loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep(loc); grouping_ = sep.grouping; @@ -2060,9 +1875,8 @@ template class digit_grouping { for (int i = 0, sep_index = static_cast(separators.size() - 1); i < num_digits; ++i) { if (num_digits - i == separators[sep_index]) { - out = - copy_str(thousands_sep_.data(), - thousands_sep_.data() + thousands_sep_.size(), out); + out = copy(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); --sep_index; } *out++ = static_cast(digits[to_unsigned(i)]); @@ -2079,54 +1893,45 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, - const format_specs& specs, - const digit_grouping& grouping) -> OutputIt { + const format_specs& specs, const digit_grouping& grouping) + -> OutputIt { static_assert(std::is_same, UInt>::value, ""); int num_digits = 0; auto buffer = memory_buffer(); - switch (specs.type) { + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { + case presentation_type::dec: num_digits = count_digits(value); format_decimal(appender(buffer), value, num_digits); break; - } - case presentation_type::hex_lower: - case presentation_type::hex_upper: { - bool upper = specs.type == presentation_type::hex_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + case presentation_type::hex: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); - format_uint<4, char>(appender(buffer), value, num_digits, upper); - break; - } - case presentation_type::bin_lower: - case presentation_type::bin_upper: { - bool upper = specs.type == presentation_type::bin_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); - num_digits = count_digits<1>(value); - format_uint<1, char>(appender(buffer), value, num_digits); + format_base2e(4, appender(buffer), value, num_digits, specs.upper()); break; - } - case presentation_type::oct: { + case presentation_type::oct: num_digits = count_digits<3>(value); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. - if (specs.alt && specs.precision <= num_digits && value != 0) + if (specs.alt() && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); - format_uint<3, char>(appender(buffer), value, num_digits); + format_base2e(3, appender(buffer), value, num_digits); + break; + case presentation_type::bin: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_base2e(1, appender(buffer), value, num_digits); break; - } case presentation_type::chr: - return write_char(out, static_cast(value), specs); - default: - throw_format_error("invalid format specifier"); + return write_char(out, static_cast(value), specs); } unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + to_unsigned(grouping.count_separators(num_digits)); - return write_padded( + return write_padded( out, specs, size, size, [&](reserve_iterator it) { for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) *it++ = static_cast(p & 0xff); @@ -2134,11 +1939,13 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, }); } +#if FMT_USE_LOCALE // Writes a localized value. -FMT_API auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool; -template -inline auto write_loc(OutputIt, loc_value, const format_specs&, +FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, + locale_ref loc) -> bool; +#endif +template +inline auto write_loc(OutputIt, const loc_value&, const format_specs&, locale_ref) -> bool { return false; } @@ -2149,7 +1956,7 @@ template struct write_int_arg { }; template -FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) +FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) -> write_int_arg> { auto prefix = 0u; auto abs_value = static_cast>(value); @@ -2159,21 +1966,21 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) } else { constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; - prefix = prefixes[sign]; + prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; } template struct loc_writer { - buffer_appender out; - const format_specs& specs; + basic_appender out; + const format_specs& specs; std::basic_string sep; std::string grouping; std::basic_string decimal_point; template ::value)> auto operator()(T value) -> bool { - auto arg = make_write_int_arg(value, specs.sign); + auto arg = make_write_int_arg(value, specs.sign()); write_int(out, static_cast>(arg.abs_value), arg.prefix, specs, digit_grouping(grouping, sep)); return true; @@ -2185,167 +1992,162 @@ template struct loc_writer { } }; +// Size and padding computation separate from write_int to avoid template bloat. +struct size_padding { + unsigned size; + unsigned padding; + + FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, + const format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align() == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, - const format_specs& specs, - locale_ref) -> OutputIt { + const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); + + constexpr int buffer_size = num_bits(); + char buffer[buffer_size]; + if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); + const char* begin = nullptr; + const char* end = buffer + buffer_size; + auto abs_value = arg.abs_value; auto prefix = arg.prefix; - switch (specs.type) { + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { - auto num_digits = count_digits(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - case presentation_type::hex_lower: - case presentation_type::hex_upper: { - bool upper = specs.type == presentation_type::hex_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); - int num_digits = count_digits<4>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, upper); - }); - } - case presentation_type::bin_lower: - case presentation_type::bin_upper: { - bool upper = specs.type == presentation_type::bin_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); - int num_digits = count_digits<1>(abs_value); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } + case presentation_type::dec: + begin = do_format_decimal(buffer, abs_value, buffer_size); + break; + case presentation_type::hex: + begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + break; case presentation_type::oct: { - int num_digits = count_digits<3>(abs_value); + begin = do_format_base2e(3, buffer, abs_value, buffer_size); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. - if (specs.alt && specs.precision <= num_digits && abs_value != 0) + auto num_digits = end - begin; + if (specs.alt() && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); + break; } + case presentation_type::bin: + begin = do_format_base2e(1, buffer, abs_value, buffer_size); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + break; case presentation_type::chr: - return write_char(out, static_cast(abs_value), specs); - default: - throw_format_error("invalid format specifier"); + return write_char(out, static_cast(abs_value), specs); } - return out; + + // Write an integer in the format + // + // prefix contains chars in three lower bytes and the size in the fourth byte. + int num_digits = static_cast(end - begin); + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return base_iterator(out, copy(begin, end, it)); + } + auto sp = size_padding(num_digits, prefix, specs); + unsigned padding = sp.padding; + return write_padded( + out, specs, sp.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, padding, static_cast('0')); + return copy(begin, end, it); + }); } + template -FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( - OutputIt out, write_int_arg arg, const format_specs& specs, - locale_ref loc) -> OutputIt { - return write_int(out, arg, specs, loc); +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, + write_int_arg arg, + const format_specs& specs) + -> OutputIt { + return write_int(out, arg, specs); } -template ::value && !std::is_same::value && - std::is_same>::value)> -FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, - loc); + !std::is_same::value)> +FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, + const format_specs& specs, locale_ref loc) + -> basic_appender { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int_noinline(out, make_write_int_arg(value, specs.sign()), + specs); } + // An inlined version of write used in format string compilation. template ::value && !std::is_same::value && - !std::is_same>::value)> + !std::is_same::value && + !std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); + const format_specs& specs, locale_ref loc) + -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int(out, make_write_int_arg(value, specs.sign()), specs); } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - FMT_UNCHECKED_ITERATOR(counting_iterator); - - struct value_type { - template FMT_CONSTEXPR void operator=(const T&) {} - }; - - FMT_CONSTEXPR counting_iterator() : count_(0) {} - - FMT_CONSTEXPR auto count() const -> size_t { return count_; } - - FMT_CONSTEXPR auto operator++() -> counting_iterator& { - ++count_; - return *this; - } - FMT_CONSTEXPR auto operator++(int) -> counting_iterator { - auto it = *this; - ++*this; - return it; - } - - FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n) - -> counting_iterator { - it.count_ += static_cast(n); - return it; - } - - FMT_CONSTEXPR auto operator*() const -> value_type { return {}; } -}; - template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, - const format_specs& specs) -> OutputIt { + const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - bool is_debug = specs.type == presentation_type::debug; + + bool is_debug = specs.type() == presentation_type::debug; + if (is_debug) { + auto buf = counting_buffer(); + write_escaped_string(basic_appender(buf), s); + size = buf.count(); + } + size_t width = 0; if (specs.width != 0) { - if (is_debug) - width = write_escaped_string(counting_iterator{}, s).count(); - else - width = compute_width(basic_string_view(data, size)); + width = + is_debug ? size : compute_width(basic_string_view(data, size)); } - return write_padded(out, specs, size, width, - [=](reserve_iterator it) { - if (is_debug) return write_escaped_string(it, s); - return copy_str(data, data + size, it); - }); + return write_padded( + out, specs, size, width, [=](reserve_iterator it) { + return is_debug ? write_escaped_string(it, s) + : copy(data, data + size, it); + }); } template -FMT_CONSTEXPR auto write(OutputIt out, - basic_string_view> s, - const format_specs& specs, locale_ref) - -> OutputIt { - return write(out, s, specs); +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs, locale_ref) -> OutputIt { + return write(out, s, specs); } template -FMT_CONSTEXPR auto write(OutputIt out, const Char* s, - const format_specs& specs, locale_ref) - -> OutputIt { - if (specs.type == presentation_type::pointer) +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, + locale_ref) -> OutputIt { + if (specs.type() == presentation_type::pointer) return write_ptr(out, bit_cast(s), &specs); - if (!s) throw_format_error("string pointer is null"); - return write(out, basic_string_view(s), specs, {}); + if (!s) report_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); } template OutputIt { if (negative) abs_value = ~abs_value + 1; int num_digits = count_digits(abs_value); auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { + if (auto ptr = to_pointer(out, size)) { if (negative) *ptr++ = static_cast('-'); format_decimal(ptr, abs_value, num_digits); return out; } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); + if (negative) *out++ = static_cast('-'); + return format_decimal(out, abs_value, num_digits); } -// DEPRECATED! template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, - format_specs& specs) -> const Char* { + format_specs& specs) -> const Char* { FMT_ASSERT(begin != end, ""); - auto align = align::none; + auto alignment = align::none; auto p = begin + code_point_length(begin); if (end - p <= 0) p = begin; for (;;) { switch (to_ascii(*p)) { - case '<': - align = align::left; - break; - case '>': - align = align::right; - break; - case '^': - align = align::center; - break; + case '<': alignment = align::left; break; + case '>': alignment = align::right; break; + case '^': alignment = align::center; break; } - if (align != align::none) { + if (alignment != align::none) { if (p != begin) { auto c = *begin; if (c == '}') return begin; if (c == '{') { - throw_format_error("invalid fill character '{'"); + report_error("invalid fill character '{'"); return begin; } - specs.fill = {begin, to_unsigned(p - begin)}; + specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); begin = p + 1; } else { ++begin; @@ -2409,88 +2202,27 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, } p = begin; } - specs.align = align; + specs.set_align(alignment); return begin; } -// 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 binary32 : 1; - bool showpoint : 1; -}; - -template -FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs) - -> float_specs { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - case presentation_type::none: - result.format = float_format::general; - break; - case presentation_type::general_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::general_lower: - result.format = float_format::general; - break; - case presentation_type::exp_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::exp_lower: - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::fixed_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::fixed_lower: - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::hexfloat_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::hexfloat_lower: - result.format = float_format::hex; - break; - default: - throw_format_error("invalid format specifier"); - break; - } - return result; -} - template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, - format_specs specs, - const float_specs& fspecs) -> OutputIt { + format_specs specs, sign s) -> OutputIt { auto str = - isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); constexpr size_t str_size = 3; - auto sign = fspecs.sign; - auto size = str_size + (sign ? 1 : 0); + auto size = str_size + (s != sign::none ? 1 : 0); // Replace '0'-padding with space for non-finite values. const bool is_zero_fill = - specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); - if (is_zero_fill) specs.fill[0] = static_cast(' '); - return write_padded(out, specs, size, [=](reserve_iterator it) { - if (sign) *it++ = detail::sign(sign); - return copy_str(str, str + str_size, it); - }); + specs.fill_size() == 1 && specs.fill_unit() == '0'; + if (is_zero_fill) specs.set_fill(' '); + return write_padded(out, specs, size, + [=](reserve_iterator it) { + if (s != sign::none) + *it++ = detail::getsign(s); + return copy(str, str + str_size, it); + }); } // A decimal floating-point number significand * pow(10, exp). @@ -2511,12 +2243,12 @@ inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { template constexpr auto write_significand(OutputIt out, const char* significand, int significand_size) -> OutputIt { - return copy_str(significand, significand + significand_size, out); + return copy(significand, significand + significand_size, out); } template inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { - return format_decimal(out, significand, significand_size).end; + return format_decimal(out, significand, significand_size); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, @@ -2536,14 +2268,13 @@ template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; + if (!decimal_point) return format_decimal(out, significand, significand_size); out += significand_size + 1; Char* end = out; int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(static_cast(significand % 100))); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2564,19 +2295,19 @@ inline auto write_significand(OutputIt out, UInt significand, Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); - return detail::copy_str_noinline(buffer, end, out); + return detail::copy_noinline(buffer, end, out); } template FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, int significand_size, int integral_size, Char decimal_point) -> OutputIt { - out = detail::copy_str_noinline(significand, - significand + integral_size, out); + out = detail::copy_noinline(significand, significand + integral_size, + out); if (!decimal_point) return out; *out++ = decimal_point; - return detail::copy_str_noinline(significand + integral_size, - significand + significand_size, out); + return detail::copy_noinline(significand + integral_size, + significand + significand_size, out); } template @@ -2589,44 +2320,42 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, decimal_point); } auto buffer = basic_memory_buffer(); - write_significand(buffer_appender(buffer), significand, - significand_size, integral_size, decimal_point); + write_significand(basic_appender(buffer), significand, significand_size, + integral_size, decimal_point); grouping.apply( out, basic_string_view(buffer.data(), to_unsigned(integral_size))); - return detail::copy_str_noinline(buffer.data() + integral_size, - buffer.end(), out); + return detail::copy_noinline(buffer.data() + integral_size, + buffer.end(), out); } -template > FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { auto significand = f.significand; int significand_size = get_significand_size(f); const Char zero = static_cast('0'); - auto sign = fspecs.sign; - size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); using iterator = reserve_iterator; - Char decimal_point = - fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + Char decimal_point = specs.localized() ? detail::decimal_point(loc) + : static_cast('.'); int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { - if (fspecs.format == float_format::exp) return true; - if (fspecs.format != float_format::general) return false; + if (specs.type() == presentation_type::exp) return true; + if (specs.type() == presentation_type::fixed) return false; // Use the fixed notation if the exponent is in [exp_lower, exp_upper), // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. - const int exp_lower = -4, exp_upper = 16; + const int exp_lower = -4; return output_exp < exp_lower || - output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); }; if (use_exp_format()) { int num_zeros = 0; - if (fspecs.showpoint) { - num_zeros = fspecs.precision - significand_size; + if (specs.alt()) { + num_zeros = specs.precision - significand_size; if (num_zeros < 0) num_zeros = 0; size += to_unsigned(num_zeros); } else if (significand_size == 1) { @@ -2637,9 +2366,9 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); - char exp_char = fspecs.upper ? 'E' : 'e'; + char exp_char = specs.upper() ? 'E' : 'e'; auto write = [=](iterator it) { - if (sign) *it++ = detail::sign(sign); + if (s != sign::none) *it++ = detail::getsign(s); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, significand, significand_size, 1, decimal_point); @@ -2647,39 +2376,41 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, *it++ = static_cast(exp_char); return write_exponent(output_exp, it); }; - return specs.width > 0 ? write_padded(out, specs, size, write) - : base_iterator(out, write(reserve(out, size))); + return specs.width > 0 + ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); } int exp = f.exponent + significand_size; if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] size += to_unsigned(f.exponent); - int num_zeros = fspecs.precision - exp; + int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); - if (fspecs.showpoint) { + if (specs.alt()) { ++size; - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; + if (num_zeros <= 0 && specs.type() != presentation_type::fixed) + num_zeros = 0; if (num_zeros > 0) size += to_unsigned(num_zeros); } - auto grouping = Grouping(loc, fspecs.locale); + auto grouping = Grouping(loc, specs.localized()); size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, significand, significand_size, f.exponent, grouping); - if (!fspecs.showpoint) return it; + if (!specs.alt()) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } else if (exp > 0) { // 1234e-2 -> 12.34[0+] - int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; - size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); - auto grouping = Grouping(loc, fspecs.locale); + int num_zeros = specs.alt() ? specs.precision - significand_size : 0; + size += 1 + static_cast(max_of(num_zeros, 0)); + auto grouping = Grouping(loc, specs.localized()); size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, significand, significand_size, exp, decimal_point, grouping); return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -2687,14 +2418,14 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, } // 1234e-6 -> 0.001234 int num_zeros = -exp; - if (significand_size == 0 && fspecs.precision >= 0 && - fspecs.precision < num_zeros) { - num_zeros = fspecs.precision; + if (significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { + num_zeros = specs.precision; } - bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); *it++ = zero; if (!pointy) return it; *it++ = decimal_point; @@ -2717,22 +2448,21 @@ template class fallback_digit_grouping { } }; -template +template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float>(out, f, specs, fspecs, - loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float(out, f, specs, fspecs, loc); + return do_write_float(out, f, specs, s, exp_upper, loc); } } template constexpr auto isnan(T value) -> bool { - return !(value >= value); // std::isnan doesn't support __float128. + return value != value; // std::isnan doesn't support __float128. } template @@ -2742,8 +2472,8 @@ template struct has_isfinite> : std::true_type {}; -template ::value&& - has_isfinite::value)> +template ::value&& has_isfinite::value)> FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) @@ -2780,52 +2510,48 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { 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; + // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. + using bigit = uint32_t; // A big digit. using double_bigit = uint64_t; + enum { bigit_bits = num_bits() }; enum { bigits_capacity = 32 }; basic_memory_buffer bigits_; int exp_; - FMT_CONSTEXPR20 auto operator[](int index) const -> bigit { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 auto operator[](int index) -> bigit& { - return bigits_[to_unsigned(index)]; - } - - static constexpr const int bigit_bits = num_bits(); - friend struct formatter; - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); + FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { + return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; + } + + FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = double_bigit(bigits_[index]) - other - borrow; + bigits_[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } - FMT_CONSTEXPR20 void remove_leading_zeros() { + FMT_CONSTEXPR void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. - FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_CONSTEXPR 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 (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); + if (borrow != 0) subtract_bigits(i, 0, borrow); + FMT_ASSERT(borrow == 0, ""); remove_leading_zeros(); } - FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; + FMT_CONSTEXPR void multiply(uint32_t value) { bigit carry = 0; + const double_bigit wide_value = value; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { double_bigit result = bigits_[i] * wide_value + carry; bigits_[i] = static_cast(result); @@ -2836,7 +2562,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void multiply(UInt value) { + FMT_CONSTEXPR void multiply(UInt value) { using half_uint = conditional_t::value, uint64_t, uint32_t>; const int shift = num_bits() - bigit_bits; @@ -2857,7 +2583,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void assign(UInt n) { + FMT_CONSTEXPR void assign(UInt n) { size_t num_bigits = 0; do { bigits_[num_bigits++] = static_cast(n); @@ -2868,30 +2594,30 @@ class bigint { } public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} + FMT_CONSTEXPR bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; - FMT_CONSTEXPR20 void assign(const bigint& other) { + FMT_CONSTEXPR void assign(const bigint& other) { auto size = other.bigits_.size(); bigits_.resize(size); auto data = other.bigits_.data(); - copy_str(data, data + size, bigits_.data()); + copy(data, data + size, bigits_.data()); exp_ = other.exp_; } - template FMT_CONSTEXPR20 void operator=(Int n) { + template FMT_CONSTEXPR void operator=(Int n) { FMT_ASSERT(n > 0, ""); assign(uint64_or_128_t(n)); } - FMT_CONSTEXPR20 auto num_bigits() const -> int { + FMT_CONSTEXPR auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } - FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& { + FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -2906,49 +2632,39 @@ class bigint { return *this; } - template - FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& { + template FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs) - -> int { - 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; + friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { + int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); + if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; + int i = static_cast(b1.bigits_.size()) - 1; + int j = static_cast(b2.bigits_.size()) - 1; int end = i - j; if (end < 0) end = 0; for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; + if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; } if (i != j) return i > j ? 1 : -1; return 0; } // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1, - const bigint& lhs2, const bigint& rhs) - -> int { - auto minimum = [](int a, int b) { return a < b ? a : b; }; - auto maximum = [](int a, int b) { return a > b ? a : b; }; - int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) -> int { + int max_lhs_bigits = max_of(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[i - n.exp_] : 0; - }; double_bigit borrow = 0; - int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + int min_exp = min_of(min_of(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); + double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); + bigit rhs_bigit = rhs.get_bigit(i); if (sum > rhs_bigit + borrow) return 1; borrow = rhs_bigit + borrow - sum; if (borrow > 1) return -1; @@ -2961,10 +2677,8 @@ class bigint { FMT_CONSTEXPR20 void assign_pow10(int exp) { FMT_ASSERT(exp >= 0, ""); if (exp == 0) return *this = 1; - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; + int bitmask = 1 << (num_bits() - + countl_zero(static_cast(exp)) - 1); // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by // repeated squaring and multiplication. *this = 5; @@ -2988,17 +2702,17 @@ class bigint { // 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]; + sum += double_bigit(n[i]) * n[j]; } - (*this)[bigit_index] = static_cast(sum); + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); // 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--]; - (*this)[bigit_index] = static_cast(sum); + sum += double_bigit(n[i++]) * n[j--]; + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); } remove_leading_zeros(); @@ -3007,20 +2721,20 @@ class bigint { // If this bigint has a bigger exponent than other, adds trailing zero to make // exponents equal. This simplifies some operations such as subtraction. - FMT_CONSTEXPR20 void align(const bigint& other) { + FMT_CONSTEXPR void align(const bigint& other) { int exp_difference = exp_ - other.exp_; if (exp_difference <= 0) return; int num_bigits = static_cast(bigits_.size()); bigits_.resize(to_unsigned(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, 0u); + memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); exp_ -= exp_difference; } // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int { + FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -3139,8 +2853,11 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Generate the given number of digits. exp10 -= num_digits - 1; if (num_digits <= 0) { - denominator *= 10; - auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + auto digit = '0'; + if (num_digits == 0) { + denominator *= 10; + digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + } buf.push_back(digit); return; } @@ -3177,8 +2894,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Formats a floating-point number using the hexfloat format. template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { // float is passed as double to reduce the number of instantiations and to // simplify implementation. static_assert(!std::is_same::value, ""); @@ -3188,26 +2905,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename info::carrier_uint; - constexpr auto num_float_significand_bits = - detail::num_significand_bits(); + const auto num_float_significand_bits = detail::num_significand_bits(); basic_fp f(value); f.e += num_float_significand_bits; if (!has_implicit_bit()) --f.e; - constexpr auto num_fraction_bits = + const auto num_fraction_bits = num_float_significand_bits + (has_implicit_bit() ? 1 : 0); - constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; + const auto num_xdigits = (num_fraction_bits + 3) / 4; - constexpr auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_shift = ((num_xdigits - 1) * 4); const auto leading_mask = carrier_uint(0xF) << leading_shift; const auto leading_xdigit = static_cast((f.f & leading_mask) >> leading_shift); if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); int print_xdigits = num_xdigits - 1; - if (precision >= 0 && print_xdigits > precision) { - const int shift = ((print_xdigits - precision - 1) * 4); + if (specs.precision >= 0 && print_xdigits > specs.precision) { + const int shift = ((print_xdigits - specs.precision - 1) * 4); const auto mask = carrier_uint(0xF) << shift; const auto v = static_cast((f.f & mask) >> shift); @@ -3226,25 +2942,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } } - print_xdigits = precision; + print_xdigits = specs.precision; } char xdigits[num_bits() / 4]; detail::fill_n(xdigits, sizeof(xdigits), '0'); - format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); + format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); // Remove zero tail while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; buf.push_back('0'); - buf.push_back(specs.upper ? 'X' : 'x'); + buf.push_back(specs.upper() ? 'X' : 'x'); buf.push_back(xdigits[0]); - if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) + if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) buf.push_back('.'); buf.append(xdigits + 1, xdigits + 1 + print_xdigits); - for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); + for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); - buf.push_back(specs.upper ? 'P' : 'p'); + buf.push_back(specs.upper() ? 'P' : 'p'); uint32_t abs_e; if (f.e < 0) { @@ -3258,9 +2974,9 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { - format_hexfloat(static_cast(value), precision, specs, buf); +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { + format_hexfloat(static_cast(value), specs, buf); } constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { @@ -3275,15 +2991,15 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { } template -FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, +FMT_CONSTEXPR20 auto format_float(Float value, int precision, + const format_specs& specs, bool binary32, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); auto converted_value = convert_float(value); - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. + const bool fixed = specs.type() == presentation_type::fixed; + if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; @@ -3308,16 +3024,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; - } else if (precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; @@ -3416,7 +3122,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, uint64_t prod; uint32_t digits; bool should_round_up; - int number_of_digits_to_print = precision > 9 ? 9 : precision; + int number_of_digits_to_print = min_of(precision, 9); // Print a 9-digits subsegment, either the first or the second. auto print_subsegment = [&](uint32_t subsegment, char* buffer) { @@ -3444,7 +3150,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // for details. prod = ((subsegment * static_cast(450359963)) >> 20) + 1; digits = static_cast(prod >> 32); - copy2(buffer, digits2(digits)); + write2digits(buffer, digits); number_of_digits_printed += 2; } @@ -3452,7 +3158,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, while (number_of_digits_printed < number_of_digits_to_print) { prod = static_cast(prod) * static_cast(100); digits = static_cast(prod >> 32); - copy2(buffer + number_of_digits_printed, digits2(digits)); + write2digits(buffer + number_of_digits_printed, digits); number_of_digits_printed += 2; } }; @@ -3561,9 +3267,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } if (use_dragon) { auto f = basic_fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(converted_value); + bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) + : f.assign(converted_value); if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; if (fixed) dragon_flags |= dragon::fixed; // Limit precision to the maximum possible number of significant digits in @@ -3572,7 +3277,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, if (precision > max_double_digits) precision = max_double_digits; format_dragon(f, dragon_flags, precision, buf, exp); } - if (!fixed && !specs.showpoint) { + if (!fixed && !specs.alt()) { // Remove trailing zeros. auto num_digits = buf.size(); while (num_digits > 0 && buf[num_digits - 1] == '0') { @@ -3583,97 +3288,106 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } return exp; } + +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template constexpr auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} + template -FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, - format_specs specs, locale_ref loc) - -> OutputIt { - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; - if (detail::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; - } +FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, + locale_ref loc) -> OutputIt { + // Use signbit because value < 0 is false for NaN. + sign s = detail::signbit(value) ? sign::minus : specs.sign(); if (!detail::isfinite(value)) - return write_nonfinite(out, detail::isnan(value), specs, fspecs); + return write_nonfinite(out, detail::isnan(value), specs, s); - if (specs.align == align::numeric && fspecs.sign) { - auto it = reserve(out, 1); - *it++ = detail::sign(fspecs.sign); - out = base_iterator(out, it); - fspecs.sign = sign::none; + if (specs.align() == align::numeric && s != sign::none) { + *out++ = detail::getsign(s); + s = sign::none; if (specs.width != 0) --specs.width; } + constexpr int exp_upper = detail::exp_upper(); + int precision = specs.precision; + if (precision < 0) { + if (specs.type() != presentation_type::none) { + precision = 6; + } else if (is_fast_float::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + using floaty = conditional_t= sizeof(double), double, float>; + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, s, exp_upper, loc); + } + } + memory_buffer buffer; - if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - format_hexfloat(convert_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, - specs); - } - int precision = specs.precision >= 0 || specs.type == presentation_type::none - ? specs.precision - : 6; - if (fspecs.format == float_format::exp) { + if (specs.type() == presentation_type::hexfloat) { + if (s != sign::none) buffer.push_back(detail::getsign(s)); + format_hexfloat(convert_float(value), specs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + + if (specs.type() == presentation_type::exp) { if (precision == max_value()) - throw_format_error("number is too big"); + report_error("number is too big"); else ++precision; - } else if (fspecs.format != float_format::fixed && precision == 0) { + if (specs.precision != 0) specs.set_alt(); + } else if (specs.type() == presentation_type::fixed) { + if (specs.precision != 0) specs.set_alt(); + } else if (precision == 0) { precision = 1; } - if (const_check(std::is_same())) fspecs.binary32 = true; - int exp = format_float(convert_float(value), precision, fspecs, buffer); - fspecs.precision = precision; + int exp = format_float(convert_float(value), precision, specs, + std::is_same(), buffer); + + specs.precision = precision; auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, f, specs, fspecs, loc); + return write_float(out, f, specs, s, exp_upper, loc); } template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, locale_ref loc = {}) -> OutputIt { - if (const_check(!is_supported_floating_point(value))) return out; - return specs.localized && write_loc(out, value, specs, loc) + return specs.localized() && write_loc(out, value, specs, loc) ? out - : write_float(out, value, specs, loc); + : write_float(out, value, specs, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) return write(out, value, format_specs()); - if (const_check(!is_supported_floating_point(value))) return out; + if (is_constant_evaluated()) return write(out, value, format_specs()); - auto fspecs = float_specs(); - if (detail::signbit(value)) { - fspecs.sign = sign::minus; - value = -value; - } + auto s = detail::signbit(value) ? sign::minus : sign::none; - constexpr auto specs = format_specs(); - using floaty = conditional_t::value, double, T>; + constexpr auto specs = format_specs(); + using floaty = conditional_t= sizeof(double), double, float>; using floaty_uint = typename dragonbox::float_info::carrier_uint; floaty_uint mask = exponent_mask(); if ((bit_cast(value) & mask) == mask) - return write_nonfinite(out, std::isnan(value), specs, fspecs); + return write_nonfinite(out, std::isnan(value), specs, s); auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, {}); + return write_float(out, dec, specs, s, exp_upper(), {}); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, format_specs()); + return write(out, value, format_specs()); } template -auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) +auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; @@ -3682,13 +3396,11 @@ auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { - auto it = reserve(out, value.size()); - it = copy_str_noinline(value.begin(), value.end(), it); - return base_iterator(out, it); + return copy_noinline(value.begin(), value.end(), out); } template ::value)> + FMT_ENABLE_IF(has_to_string_view::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -3696,10 +3408,8 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, - bool check = - std::is_enum::value && !std::is_same::value && - mapped_type_constant>::value != - type::custom_type, + bool check = std::is_enum::value && !std::is_same::value && + mapped_type_constant::value != type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write(out, static_cast>(value)); @@ -3707,13 +3417,12 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { template ::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value, - const format_specs& specs = {}, locale_ref = {}) - -> OutputIt { - return specs.type != presentation_type::none && - specs.type != presentation_type::string - ? write(out, value ? 1 : 0, specs, {}) - : write_bytes(out, value ? "true" : "false", specs); +FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type() != presentation_type::none && + specs.type() != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); } template @@ -3724,171 +3433,150 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { } template -FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) - -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); - throw_format_error("string pointer is null"); + report_error("string pointer is null"); return out; } template ::value)> -auto write(OutputIt out, const T* value, const format_specs& specs = {}, +auto write(OutputIt out, const T* value, const format_specs& specs = {}, locale_ref = {}) -> OutputIt { return write_ptr(out, bit_cast(value), &specs); } -// A write overload that handles implicit conversions. template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< - std::is_class::value && !is_string::value && - !is_floating_point::value && !std::is_same::value && - !std::is_same().map( - value))>>::value, - OutputIt> { - return write(out, arg_mapper().map(value)); + FMT_ENABLE_IF(mapped_type_constant::value == + type::custom_type && + !std::is_fundamental::value)> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { + auto f = formatter(); + auto parse_ctx = parse_context({}); + f.parse(parse_ctx); + auto ctx = basic_format_context(out, {}, {}); + return f.format(value, ctx); } -template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) - -> enable_if_t::value == type::custom_type, - OutputIt> { - auto formatter = typename Context::template formatter_type(); - auto parse_ctx = typename Context::parse_context_type({}); - formatter.parse(parse_ctx); - auto ctx = Context(out, {}, {}); - return formatter.format(value, ctx); -} +template +using is_builtin = + bool_constant::value || FMT_BUILTIN_TYPES>; // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. template struct default_arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; + using context = buffered_context; + + basic_appender out; - iterator out; - basic_format_args args; - locale_ref loc; + void operator()(monostate) { report_error("argument not found"); } - template auto operator()(T value) -> iterator { - return write(out, value); + template ::value)> + void operator()(T value) { + write(out, value); } - auto operator()(typename basic_format_arg::handle h) -> iterator { - basic_format_parse_context parse_ctx({}); - context format_ctx(out, args, loc); + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg::handle h) { + // Use a null locale since the default format must be unlocalized. + auto parse_ctx = parse_context({}); + auto format_ctx = context(out, {}, {}); h.format(parse_ctx, format_ctx); - return format_ctx.out(); } }; template struct arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; - - iterator out; - const format_specs& specs; - locale_ref locale; + basic_appender out; + const format_specs& specs; + FMT_NO_UNIQUE_ADDRESS locale_ref locale; - template - FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { - return detail::write(out, value, specs, locale); - } - auto operator()(typename basic_format_arg::handle) -> iterator { - // User-defined types are handled separately because they require access - // to the parse context. - return out; + template ::value)> + FMT_CONSTEXPR FMT_INLINE void operator()(T value) { + detail::write(out, value, specs, locale); } -}; -struct width_checker { - template ::value)> - FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative width"); - return static_cast(value); + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); } - template ::value)> - FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("width is not integer"); - return 0; + void operator()(typename basic_format_arg>::handle) { + // User-defined types are handled separately because they require access + // to the parse context. } }; -struct precision_checker { +struct dynamic_spec_getter { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative precision"); - return static_cast(value); + return is_negative(value) ? ~0ull : static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("precision is not integer"); + report_error("width/precision is not integer"); return 0; } }; -template -FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int { - unsigned long long value = visit_format_arg(Handler(), arg); - if (value > to_unsigned(max_value())) - throw_format_error("number is too big"); - return static_cast(value); -} - template -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) { +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { auto arg = ctx.arg(id); - if (!arg) ctx.on_error("argument not found"); + if (!arg) report_error("argument not found"); return arg; } -template -FMT_CONSTEXPR void handle_dynamic_spec(int& value, - arg_ref ref, - Context& ctx) { - switch (ref.kind) { - case arg_id_kind::none: - break; - case arg_id_kind::index: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.index)); - break; - case arg_id_kind::name: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.name)); - break; - } +template +FMT_CONSTEXPR int get_dynamic_spec( + arg_id_kind kind, const arg_ref& ref, + Context& ctx) { + FMT_ASSERT(kind != arg_id_kind::none, ""); + auto arg = + kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); + if (!arg) report_error("argument not found"); + unsigned long long value = arg.visit(dynamic_spec_getter()); + if (value > to_unsigned(max_value())) + report_error("width/precision is out of range"); + return static_cast(value); +} + +template +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref& ref, Context& ctx) { + if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); } -#if FMT_USE_USER_DEFINED_LITERALS -# if FMT_USE_NONTYPE_TEMPLATE_ARGS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> -struct statically_named_arg : view { + fmt::detail::fixed_string Str> +struct static_named_arg : view { static constexpr auto name = Str.data; const T& value; - statically_named_arg(const T& v) : value(v) {} + static_named_arg(const T& v) : value(v) {} }; template Str> -struct is_named_arg> : std::true_type {}; + fmt::detail::fixed_string Str> +struct is_named_arg> : std::true_type {}; template Str> -struct is_statically_named_arg> - : std::true_type {}; + fmt::detail::fixed_string Str> +struct is_static_named_arg> : std::true_type { +}; -template Str> +template Str> struct udl_arg { template auto operator=(T&& value) const { - return statically_named_arg(std::forward(value)); + return static_named_arg(std::forward(value)); } }; -# else +#else template struct udl_arg { const Char* str; @@ -3896,149 +3584,198 @@ template struct udl_arg { return {str, std::forward(value)}; } }; -# endif -#endif // FMT_USE_USER_DEFINED_LITERALS +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS -template -auto vformat(const Locale& loc, basic_string_view fmt, - basic_format_args>> args) - -> std::basic_string { - auto buf = basic_memory_buffer(); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); - return {buf.data(), buf.size()}; -} +template struct format_handler { + parse_context parse_ctx; + buffered_context ctx; -using format_func = void (*)(detail::buffer&, int, const char*); + void on_text(const Char* begin, const Char* end) { + copy_noinline(begin, end, ctx.out()); + } + + FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + parse_ctx.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + parse_ctx.check_arg_id(id); + int arg_id = ctx.arg_id(id); + if (arg_id < 0) report_error("argument not found"); + return arg_id; + } + + FMT_INLINE void on_replacement_field(int id, const Char*) { + ctx.arg(id).visit(default_arg_formatter{ctx.out()}); + } + + auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + auto arg = get_arg(ctx, id); + // Not using a visitor for custom types gives better codegen. + if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); + + auto specs = dynamic_format_specs(); + begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type()); + if (specs.dynamic()) { + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + arg.visit(arg_formatter{ctx.out(), specs, ctx.locale()}); + return begin; + } + + FMT_NORETURN void on_error(const char* message) { report_error(message); } +}; + +using format_func = void (*)(detail::buffer&, int, const char*); +FMT_API void do_report_error(format_func func, int error_code, + const char* message) noexcept; FMT_API void format_error_code(buffer& out, int error_code, string_view message) noexcept; -FMT_API void report_error(format_func func, int error_code, - const char* message) noexcept; -} // namespace detail +template +template +FMT_CONSTEXPR auto native_formatter::format( + const T& val, FormatContext& ctx) const -> decltype(ctx.out()) { + if (!specs_.dynamic()) + return write(ctx.out(), val, specs_, ctx.locale()); + auto specs = format_specs(specs_); + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs_.precision_ref, ctx); + return write(ctx.out(), val, specs, ctx.locale()); +} + +// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; -FMT_API auto vsystem_error(int error_code, string_view format_str, - format_args args) -> std::system_error; +// DEPRECATED! +template struct vformat_args { + using type = basic_format_args>; +}; +template <> struct vformat_args { + using type = format_args; +}; -/** - \rst - Constructs :class:`std::system_error` with a message formatted with - ``fmt::format(fmt, args...)``. - *error_code* is a system error code as given by ``errno``. - - **Example**:: - - // This throws std::system_error with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char* filename = "madeup"; - std::FILE* file = std::fopen(filename, "r"); - if (!file) - throw fmt::system_error(errno, "cannot open file '{}'", filename); - \endrst - */ -template -auto system_error(int error_code, format_string fmt, T&&... args) - -> std::system_error { - return vsystem_error(error_code, fmt, fmt::make_format_args(args...)); +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}) { + auto out = basic_appender(buf); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); } +} // namespace detail -/** - \rst - Formats an error message for an error returned by an operating system or a - language runtime, for example a file opening error, and writes it to *out*. - The format is the same as the one used by ``std::system_error(ec, message)`` - where ``ec`` is ``std::error_code(error_code, std::generic_category()})``. - It is implementation-defined but normally looks like: - - .. parsed-literal:: - **: ** - - where ** is the passed message and ** is the system - message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - \endrst - */ -FMT_API void format_system_error(detail::buffer& out, int error_code, - const char* message) noexcept; - -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -FMT_API void report_system_error(int error_code, const char* message) noexcept; +FMT_BEGIN_EXPORT -/** Fast integer formatter. */ -class format_int { +// A generic formatting context with custom output iterator and character +// (code unit) support. Char is the format string code unit type which can be +// different from OutputIt::value_type. +template class generic_context { private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum { buffer_size = std::numeric_limits::digits10 + 3 }; - mutable char buffer_[buffer_size]; - char* str_; + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; - template auto format_unsigned(UInt value) -> char* { - auto n = static_cast>(value); - return detail::format_decimal(buffer_, n, buffer_size - 1).begin; + public: + using char_type = Char; + using iterator = OutputIt; + using parse_context_type FMT_DEPRECATED = parse_context; + template + using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + constexpr generic_context(OutputIt out, + basic_format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + generic_context(generic_context&&) = default; + generic_context(const generic_context&) = delete; + void operator=(const generic_context&) = delete; + + constexpr auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } + auto arg(basic_string_view name) const + -> basic_format_arg { + return args_.get(name); + } + constexpr auto arg_id(basic_string_view name) const -> int { + return args_.get_id(name); } - template auto format_signed(Int value) -> char* { - auto abs_value = static_cast>(value); - bool negative = value < 0; - if (negative) abs_value = 0 - abs_value; - auto begin = format_unsigned(abs_value); - if (negative) *--begin = '-'; - return begin; + constexpr auto out() const -> iterator { return out_; } + + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; } + constexpr auto locale() const -> detail::locale_ref { return loc_; } +}; + +class loc_value { + private: + basic_format_arg value_; + public: - explicit format_int(int value) : str_(format_signed(value)) {} - explicit format_int(long value) : str_(format_signed(value)) {} - explicit format_int(long long value) : str_(format_signed(value)) {} - explicit format_int(unsigned value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long long value) - : str_(format_unsigned(value)) {} + template ::value)> + loc_value(T value) : value_(value) {} - /** Returns the number of characters written to the output buffer. */ - auto size() const -> size_t { - return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return value_.visit(vis); } +}; + +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - auto data() const -> const char* { return str_; } + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs& specs) const -> bool; - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - auto c_str() const -> const char* { - buffer_[buffer_size - 1] = '\0'; - return str_; - } + public: + static FMT_API typename Locale::id id; - /** - \rst - Returns the content of the output buffer as an ``std::string``. - \endrst - */ - auto str() const -> std::string { return std::string(str_, size()); } -}; + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", std::string grouping = "\3", + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(grouping), + decimal_point_(decimal_point) {} -template -struct formatter::value>> - : formatter, Char> { - template - auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { - using base = formatter, Char>; - return base::format(format_as(value), ctx); + auto put(appender out, loc_value val, const format_specs& specs) const + -> bool { + return do_put(out, val, specs); } }; -#define FMT_FORMAT_AS(Type, Base) \ - template \ - struct formatter : formatter {} +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(value, ctx); \ + } \ + } FMT_FORMAT_AS(signed char, int); FMT_FORMAT_AS(unsigned char, unsigned); @@ -4047,44 +3784,58 @@ FMT_FORMAT_AS(unsigned short, unsigned); FMT_FORMAT_AS(long, detail::long_type); FMT_FORMAT_AS(unsigned long, detail::ulong_type); FMT_FORMAT_AS(Char*, const Char*); -FMT_FORMAT_AS(std::basic_string, basic_string_view); -FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(detail::std_string_view, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(void*, const void*); template struct formatter : formatter, Char> {}; -/** - \rst - Converts ``p`` to ``const void*`` for pointer formatting. +template +class formatter, Char> + : public formatter, Char> {}; - **Example**:: +template +struct formatter, Char> : formatter {}; +template +struct formatter, Char> + : formatter {}; - auto s = fmt::format("{}", fmt::ptr(p)); - \endrst +template +struct formatter + : detail::native_formatter {}; + +template +struct formatter>> + : formatter, Char> { + template + FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto&& val = format_as(value); // Make an lvalue reference for format. + return formatter, Char>::format(val, ctx); + } +}; + +/** + * Converts `p` to `const void*` for pointer formatting. + * + * **Example**: + * + * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { static_assert(std::is_pointer::value, ""); return detail::bit_cast(p); } -template -auto ptr(const std::unique_ptr& p) -> const void* { - return p.get(); -} -template auto ptr(const std::shared_ptr& p) -> const void* { - return p.get(); -} /** - \rst - Converts ``e`` to the underlying type. - - **Example**:: - - enum class color { red, green, blue }; - auto s = fmt::format("{}", fmt::underlying(color::red)); - \endrst + * Converts `e` to the underlying type. + * + * **Example**: + * + * enum class color { red, green, blue }; + * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0" */ template constexpr auto underlying(Enum e) noexcept -> underlying_t { @@ -4098,13 +3849,22 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t { } } // namespace enums -class bytes { - private: - string_view data_; - friend struct formatter; +#ifdef __cpp_lib_byte +template <> struct formatter : formatter { + static auto format_as(std::byte b) -> unsigned char { + return static_cast(b); + } + template + auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { + return formatter::format(format_as(b), ctx); + } +}; +#endif - public: - explicit bytes(string_view data) : data_(data) {} +struct bytes { + string_view data; + + inline explicit bytes(string_view s) : data(s) {} }; template <> struct formatter { @@ -4112,19 +3872,19 @@ template <> struct formatter { detail::dynamic_format_specs<> specs_; public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::string_type); } template - auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) { - detail::handle_dynamic_spec(specs_.width, - specs_.width_ref, ctx); - detail::handle_dynamic_spec( - specs_.precision, specs_.precision_ref, ctx); - return detail::write_bytes(ctx.out(), b.data_, specs_); + auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + return detail::write_bytes(ctx.out(), b.data, specs); } }; @@ -4134,15 +3894,13 @@ template struct group_digits_view { }; /** - \rst - Returns a view that formats an integer value using ',' as a locale-independent - thousands separator. - - **Example**:: - - fmt::print("{}", fmt::group_digits(12345)); - // Output: "12,345" - \endrst + * Returns a view that formats an integer value using ',' as a + * locale-independent thousands separator. + * + * **Example**: + * + * fmt::print("{}", fmt::group_digits(12345)); + * // Output: "12,345" */ template auto group_digits(T value) -> group_digits_view { return {value}; @@ -4153,331 +3911,255 @@ template struct formatter> : formatter { detail::dynamic_format_specs<> specs_; public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::int_type); } template - auto format(group_digits_view t, FormatContext& ctx) + auto format(group_digits_view view, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::handle_dynamic_spec(specs_.width, - specs_.width_ref, ctx); - detail::handle_dynamic_spec( - specs_.precision, specs_.precision_ref, ctx); + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + auto arg = detail::make_write_int_arg(view.value, specs.sign()); return detail::write_int( - ctx.out(), static_cast>(t.value), 0, specs_, - detail::digit_grouping("\3", ",")); + ctx.out(), static_cast>(arg.abs_value), + arg.prefix, specs, detail::digit_grouping("\3", ",")); } }; -template struct nested_view { - const formatter* fmt; +template struct nested_view { + const formatter* fmt; const T* value; }; -template struct formatter> { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { +template +struct formatter, Char> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } - auto format(nested_view view, format_context& ctx) const + template + auto format(nested_view view, FormatContext& ctx) const -> decltype(ctx.out()) { return view.fmt->format(*view.value, ctx); } }; -template struct nested_formatter { +template struct nested_formatter { private: + basic_specs specs_; int width_; - detail::fill_t fill_; - align_t align_ : 4; - formatter formatter_; + formatter formatter_; public: - constexpr nested_formatter() : width_(0), align_(align_t::none) {} - - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { - auto specs = detail::dynamic_format_specs(); - auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx, - detail::type::none_type); - width_ = specs.width; - fill_ = specs.fill; - align_ = specs.align; + constexpr nested_formatter() : width_(0) {} + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + auto specs = format_specs(); + it = detail::parse_align(it, end, specs); + specs_ = specs; + Char c = *it; + auto width_ref = detail::arg_ref(); + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs, width_ref, ctx); + width_ = specs.width; + } ctx.advance_to(it); return formatter_.parse(ctx); } - template - auto write_padded(format_context& ctx, F write) const -> decltype(ctx.out()) { + template + auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) { if (width_ == 0) return write(ctx.out()); - auto buf = memory_buffer(); - write(std::back_inserter(buf)); - auto specs = format_specs<>(); + auto buf = basic_memory_buffer(); + write(basic_appender(buf)); + auto specs = format_specs(); specs.width = width_; - specs.fill = fill_; - specs.align = align_; - return detail::write(ctx.out(), string_view(buf.data(), buf.size()), specs); - } - - auto nested(const T& value) const -> nested_view { - return nested_view{&formatter_, &value}; + specs.copy_fill_from(specs_); + specs.set_align(specs_.align()); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } -}; - -// DEPRECATED! join_view will be moved to ranges.h. -template -struct join_view : detail::view { - It begin; - Sentinel end; - basic_string_view sep; - - join_view(It b, Sentinel e, basic_string_view s) - : begin(b), end(e), sep(s) {} -}; - -template -struct formatter, Char> { - private: - using value_type = -#ifdef __cpp_lib_ranges - std::iter_value_t; -#else - typename std::iterator_traits::value_type; -#endif - formatter, Char> value_formatter_; - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - return value_formatter_.parse(ctx); - } - - template - auto format(const join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { - auto it = value.begin; - auto out = ctx.out(); - if (it != value.end) { - out = value_formatter_.format(*it, ctx); - ++it; - while (it != value.end) { - out = detail::copy_str(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - out = value_formatter_.format(*it, ctx); - ++it; - } - } - return out; + auto nested(const T& value) const -> nested_view { + return nested_view{&formatter_, &value}; } }; -/** - Returns a view that formats the iterator range `[begin, end)` with elements - separated by `sep`. - */ -template -auto join(It begin, Sentinel end, string_view sep) -> join_view { - return {begin, end, sep}; +inline namespace literals { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template constexpr auto operator""_a() { + using char_t = remove_cvref_t; + return detail::udl_arg(); } - +#else /** - \rst - Returns a view that formats `range` with elements separated by `sep`. - - **Example**:: - - std::vector v = {1, 2, 3}; - fmt::print("{}", fmt::join(v, ", ")); - // Output: "1, 2, 3" - - ``fmt::join`` applies passed format specifiers to the range elements:: - - fmt::print("{:02}", fmt::join(v, ", ")); - // Output: "01, 02, 03" - \endrst + * User-defined literal equivalent of `fmt::arg`. + * + * **Example**: + * + * using namespace fmt::literals; + * fmt::print("The answer is {answer}.", "answer"_a=42); */ -template -auto join(Range&& range, string_view sep) - -> join_view, detail::sentinel_t> { - return join(std::begin(range), std::end(range), sep); +constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { + return {s}; } +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS +} // namespace literals -/** - \rst - Converts *value* to ``std::string`` using the default format for type *T*. - - **Example**:: - - #include - - std::string answer = fmt::to_string(42); - \endrst - */ -template ::value && - !detail::has_format_as::value)> -inline auto to_string(const T& value) -> std::string { - auto buffer = memory_buffer(); - detail::write(appender(buffer), value); - return {buffer.data(), buffer.size()}; -} +/// A fast integer formatter. +class format_int { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum { buffer_size = std::numeric_limits::digits10 + 3 }; + mutable char buffer_[buffer_size]; + char* str_; -template ::value)> -FMT_NODISCARD inline auto to_string(T value) -> std::string { - // The buffer should be large enough to store the number including the sign - // or "false" for bool. - constexpr int max_size = detail::digits10() + 2; - char buffer[max_size > 5 ? static_cast(max_size) : 5]; - char* begin = buffer; - return std::string(begin, detail::write(begin, value)); -} + template + FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* { + auto n = static_cast>(value); + return detail::do_format_decimal(buffer_, n, buffer_size - 1); + } -template -FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) - -> std::basic_string { - auto size = buf.size(); - detail::assume(size < std::basic_string().max_size()); - return std::basic_string(buf.data(), size); -} + template + FMT_CONSTEXPR20 auto format_signed(Int value) -> char* { + auto abs_value = static_cast>(value); + bool negative = value < 0; + if (negative) abs_value = 0 - abs_value; + auto begin = format_unsigned(abs_value); + if (negative) *--begin = '-'; + return begin; + } -template ::value && - detail::has_format_as::value)> -inline auto to_string(const T& value) -> std::string { - return to_string(format_as(value)); -} + public: + FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long long value) + : str_(format_unsigned(value)) {} -FMT_END_EXPORT + /// Returns the number of characters written to the output buffer. + FMT_CONSTEXPR20 auto size() const -> size_t { + return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + } -namespace detail { + /// Returns a pointer to the output buffer content. No terminating null + /// character is appended. + FMT_CONSTEXPR20 auto data() const -> const char* { return str_; } -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc) { - auto out = buffer_appender(buf); - if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { - auto arg = args.get(0); - if (!arg) throw_format_error("argument not found"); - visit_format_arg(default_arg_formatter{out, args, loc}, arg); - return; + /// Returns a pointer to the output buffer content with terminating null + /// character appended. + FMT_CONSTEXPR20 auto c_str() const -> const char* { + buffer_[buffer_size - 1] = '\0'; + return str_; } - struct format_handler : error_handler { - basic_format_parse_context parse_context; - buffer_context context; - - format_handler(buffer_appender p_out, basic_string_view str, - basic_format_args> p_args, - locale_ref p_loc) - : parse_context(str), context(p_out, p_args, p_loc) {} + /// Returns the content of the output buffer as an `std::string`. + inline auto str() const -> std::string { return {str_, size()}; } +}; - void on_text(const Char* begin, const Char* end) { - auto text = basic_string_view(begin, to_unsigned(end - begin)); - context.advance_to(write(context.out(), text)); - } +#define FMT_STRING_IMPL(s, base) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + constexpr explicit operator fmt::basic_string_view() const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() - FMT_CONSTEXPR auto on_arg_id() -> int { - return parse_context.next_arg_id(); - } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return parse_context.check_arg_id(id), id; - } - FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { - int arg_id = context.arg_id(id); - if (arg_id < 0) throw_format_error("argument not found"); - return arg_id; - } +/** + * Constructs a legacy compile-time format string from a string literal `s`. + * + * **Example**: + * + * // A compile-time error because 'd' is an invalid specifier for strings. + * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) - FMT_INLINE void on_replacement_field(int id, const Char*) { - auto arg = get_arg(context, id); - context.advance_to(visit_format_arg( - default_arg_formatter{context.out(), context.args(), - context.locale()}, - arg)); - } +FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error; - auto on_format_specs(int id, const Char* begin, const Char* end) - -> const Char* { - auto arg = get_arg(context, id); - // Not using a visitor for custom types gives better codegen. - if (arg.format_custom(begin, parse_context, context)) - return parse_context.begin(); - auto specs = detail::dynamic_format_specs(); - begin = parse_format_specs(begin, end, specs, parse_context, arg.type()); - detail::handle_dynamic_spec( - specs.width, specs.width_ref, context); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, context); - if (begin == end || *begin != '}') - throw_format_error("missing '}' in format string"); - auto f = arg_formatter{context.out(), specs, context.locale()}; - context.advance_to(visit_format_arg(f, arg)); - return begin; - } - }; - detail::parse_format_string(fmt, format_handler(out, fmt, args, loc)); +/** + * Constructs `std::system_error` with a message formatted with + * `fmt::format(fmt, args...)`. + * `error_code` is a system error code as given by `errno`. + * + * **Example**: + * + * // This throws std::system_error with the description + * // cannot open file 'madeup': No such file or directory + * // or similar (system message may vary). + * const char* filename = "madeup"; + * FILE* file = fopen(filename, "r"); + * if (!file) + * throw fmt::system_error(errno, "cannot open file '{}'", filename); + */ +template +auto system_error(int error_code, format_string fmt, T&&... args) + -> std::system_error { + return vsystem_error(error_code, fmt.str, vargs{{args...}}); } -FMT_BEGIN_EXPORT - -#ifndef FMT_HEADER_ONLY -extern template FMT_API void vformat_to(buffer&, string_view, - typename vformat_args<>::type, - locale_ref); -extern template FMT_API auto thousands_sep_impl(locale_ref) - -> thousands_sep_result; -extern template FMT_API auto thousands_sep_impl(locale_ref) - -> thousands_sep_result; -extern template FMT_API auto decimal_point_impl(locale_ref) -> char; -extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -#endif // FMT_HEADER_ONLY - -} // namespace detail - -#if FMT_USE_USER_DEFINED_LITERALS -inline namespace literals { /** - \rst - User-defined literal equivalent of :func:`fmt::arg`. - - **Example**:: - - using namespace fmt::literals; - fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); - \endrst + * Formats an error message for an error returned by an operating system or a + * language runtime, for example a file opening error, and writes it to `out`. + * The format is the same as the one used by `std::system_error(ec, message)` + * where `ec` is `std::error_code(error_code, std::generic_category())`. + * It is implementation-defined but normally looks like: + * + * : + * + * where `` is the passed message and `` is the system + * message corresponding to the error code. + * `error_code` is a system error code as given by `errno`. */ -# if FMT_USE_NONTYPE_TEMPLATE_ARGS -template constexpr auto operator""_a() { - using char_t = remove_cvref_t; - return detail::udl_arg(); -} -# else -constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { - return {s}; -} -# endif -} // namespace literals -#endif // FMT_USE_USER_DEFINED_LITERALS +FMT_API void format_system_error(detail::buffer& out, int error_code, + const char* message) noexcept; + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_system_error(int error_code, const char* message) noexcept; template ::value)> inline auto vformat(const Locale& loc, string_view fmt, format_args args) -> std::string { - return detail::vformat(loc, fmt, args); + auto buf = memory_buffer(); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return {buf.data(), buf.size()}; } template ::value)> -inline auto format(const Locale& loc, format_string fmt, T&&... args) +FMT_INLINE auto format(const Locale& loc, format_string fmt, T&&... args) -> std::string { - return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...)); + return vformat(loc, fmt.str, vargs{{args...}}); } template ::value&& - detail::is_locale::value)> + FMT_ENABLE_IF(detail::is_output_iterator::value)> auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, format_args args) -> OutputIt { - using detail::get_buffer; - auto&& buf = get_buffer(out); + auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); return detail::get_iterator(buf, out); } @@ -4487,7 +4169,7 @@ template ::value)> FMT_INLINE auto format_to(OutputIt out, const Locale& loc, format_string fmt, T&&... args) -> OutputIt { - return vformat_to(out, loc, fmt, fmt::make_format_args(args...)); + return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); } template fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt, fmt::make_format_args(args...), - detail::locale_ref(loc)); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, + detail::locale_ref(loc)); return buf.count(); } -FMT_END_EXPORT +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; -template -template -FMT_CONSTEXPR FMT_INLINE auto -formatter::value != - detail::type::custom_type>>::format(const T& val, - FormatContext& ctx) - const -> decltype(ctx.out()) { - if (specs_.width_ref.kind == detail::arg_id_kind::none && - specs_.precision_ref.kind == detail::arg_id_kind::none) { - return detail::write(ctx.out(), val, specs_, ctx.locale()); - } - auto specs = specs_; - detail::handle_dynamic_spec(specs.width, - specs.width_ref, ctx); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, ctx); - return detail::write(ctx.out(), val, specs, ctx.locale()); +/** + * Formats `args` according to specifications in `fmt` and returns the result + * as a string. + * + * **Example**: + * + * #include + * std::string message = fmt::format("The answer is {}.", 42); + */ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt.str, vargs{{args...}}); +} + +/** + * Converts `value` to `std::string` using the default format for type `T`. + * + * **Example**: + * + * std::string answer = fmt::to_string(42); + */ +template ::value)> +FMT_NODISCARD auto to_string(T value) -> std::string { + // The buffer should be large enough to store the number including the sign + // or "false" for bool. + char buffer[max_of(detail::digits10() + 2, 5)]; + return {buffer, detail::write(buffer, value)}; } +template ::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + return to_string(format_as(value)); +} + +template ::value && + !detail::use_format_as::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + auto buffer = memory_buffer(); + detail::write(appender(buffer), value); + return {buffer.data(), buffer.size()}; +} + +FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" -#else -# define FMT_FUNC +#endif + +// Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES. +#ifdef FMT_REMOVE_TRANSITIVE_INCLUDES +# undef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES #endif #endif // FMT_FORMAT_H_ diff --git a/deps/spdlog/include/spdlog/fmt/bundled/locale.h b/deps/spdlog/include/spdlog/fmt/bundled/locale.h deleted file mode 100644 index 7571b5261ba..00000000000 --- a/deps/spdlog/include/spdlog/fmt/bundled/locale.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "xchar.h" -#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead diff --git a/deps/spdlog/include/spdlog/fmt/bundled/os.h b/deps/spdlog/include/spdlog/fmt/bundled/os.h index 3c7b3ccb481..b2cc5e4b85f 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/os.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/os.h @@ -8,18 +8,18 @@ #ifndef FMT_OS_H_ #define FMT_OS_H_ -#include -#include -#include -#include // std::system_error - #include "format.h" -#if defined __APPLE__ || defined(__FreeBSD__) +#ifndef FMT_MODULE +# include +# include +# include +# include // std::system_error + # if FMT_HAS_INCLUDE() -# include // for LC_NUMERIC_MASK on OS X +# include // LC_NUMERIC_MASK on macOS # endif -#endif +#endif // FMT_MODULE #ifndef FMT_USE_FCNTL // UWP doesn't provide _pipe. @@ -77,46 +77,33 @@ FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT /** - \rst - A reference to a null-terminated string. It can be constructed from a C - string or ``std::string``. - - You can use one of the following type aliases for common character types: - - +---------------+-----------------------------+ - | Type | Definition | - +===============+=============================+ - | cstring_view | basic_cstring_view | - +---------------+-----------------------------+ - | wcstring_view | basic_cstring_view | - +---------------+-----------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(cstring_view format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst + * A reference to a null-terminated string. It can be constructed from a C + * string or `std::string`. + * + * You can use one of the following type aliases for common character types: + * + * +---------------+-----------------------------+ + * | Type | Definition | + * +===============+=============================+ + * | cstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * | wcstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * + * This class is most useful as a parameter type for functions that wrap C APIs. */ template class basic_cstring_view { private: const Char* data_; public: - /** Constructs a string reference object from a C string. */ + /// Constructs a string reference object from a C string. basic_cstring_view(const Char* s) : data_(s) {} - /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ + /// Constructs a string reference from an `std::string` object. basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} - /** Returns the pointer to a C string. */ + /// Returns the pointer to a C string. auto c_str() const -> const Char* { return data_; } }; @@ -131,41 +118,38 @@ FMT_API void format_windows_error(buffer& out, int error_code, const char* message) noexcept; } -FMT_API std::system_error vwindows_error(int error_code, string_view format_str, +FMT_API std::system_error vwindows_error(int error_code, string_view fmt, format_args args); /** - \rst - Constructs a :class:`std::system_error` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the - system message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a system_error with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) { - throw fmt::windows_error(GetLastError(), - "cannot open file '{}'", filename); - } - \endrst -*/ -template -std::system_error windows_error(int error_code, string_view message, - const Args&... args) { - return vwindows_error(error_code, message, fmt::make_format_args(args...)); + * Constructs a `std::system_error` object with the description of the form + * + * : + * + * where `` is the formatted message and `` is the + * system message corresponding to the error code. + * `error_code` is a Windows error code as given by `GetLastError`. + * If `error_code` is not a valid error code such as -1, the system message + * will look like "error -1". + * + * **Example**: + * + * // This throws a system_error with the description + * // cannot open file 'madeup': The system cannot find the file + * specified. + * // or similar (system message may vary). + * const char *filename = "madeup"; + * LPOFSTRUCT of = LPOFSTRUCT(); + * HFILE file = OpenFile(filename, &of, OF_READ); + * if (file == HFILE_ERROR) { + * throw fmt::windows_error(GetLastError(), + * "cannot open file '{}'", filename); + * } + */ +template +auto windows_error(int error_code, string_view message, const T&... args) + -> std::system_error { + return vwindows_error(error_code, message, vargs{{args...}}); } // Reports a Windows error without throwing an exception. @@ -180,8 +164,8 @@ inline auto system_category() noexcept -> const std::error_category& { // std::system is not available on some platforms such as iOS (#2248). #ifdef __OSX__ template > -void say(const S& format_str, Args&&... args) { - std::system(format("say \"{}\"", format(format_str, args...)).c_str()); +void say(const S& fmt, Args&&... args) { + std::system(format("say \"{}\"", format(fmt, args...)).c_str()); } #endif @@ -192,24 +176,24 @@ class buffered_file { friend class file; - explicit buffered_file(FILE* f) : file_(f) {} + inline explicit buffered_file(FILE* f) : file_(f) {} public: buffered_file(const buffered_file&) = delete; void operator=(const buffered_file&) = delete; // Constructs a buffered_file object which doesn't represent any file. - buffered_file() noexcept : file_(nullptr) {} + inline buffered_file() noexcept : file_(nullptr) {} // Destroys the object closing the file it represents if any. FMT_API ~buffered_file() noexcept; public: - buffered_file(buffered_file&& other) noexcept : file_(other.file_) { + inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { other.file_ = nullptr; } - auto operator=(buffered_file&& other) -> buffered_file& { + inline auto operator=(buffered_file&& other) -> buffered_file& { close(); file_ = other.file_; other.file_ = nullptr; @@ -223,21 +207,20 @@ class buffered_file { FMT_API void close(); // Returns the pointer to a FILE object representing this file. - auto get() const noexcept -> FILE* { return file_; } + inline auto get() const noexcept -> FILE* { return file_; } FMT_API auto descriptor() const -> int; - void vprint(string_view format_str, format_args args) { - fmt::vprint(file_, format_str, args); - } - - template - inline void print(string_view format_str, const Args&... args) { - vprint(format_str, fmt::make_format_args(args...)); + template + inline void print(string_view fmt, const T&... args) { + fmt::vargs vargs = {{args...}}; + detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) + : fmt::vprint(file_, fmt, vargs); } }; #if FMT_USE_FCNTL + // A file. Closed file is represented by a file object with descriptor -1. // Methods that are not declared with noexcept may throw // fmt::system_error in case of failure. Note that some errors such as @@ -251,6 +234,8 @@ class FMT_API file { // Constructs a file object with a given descriptor. explicit file(int fd) : fd_(fd) {} + friend struct pipe; + public: // Possible values for the oflag argument to the constructor. enum { @@ -263,7 +248,7 @@ class FMT_API file { }; // Constructs a file object which doesn't represent any file. - file() noexcept : fd_(-1) {} + inline file() noexcept : fd_(-1) {} // Opens a file and constructs a file object representing this file. file(cstring_view path, int oflag); @@ -272,10 +257,10 @@ class FMT_API file { file(const file&) = delete; void operator=(const file&) = delete; - file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } + inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } // Move assignment is not noexcept because close may throw. - auto operator=(file&& other) -> file& { + inline auto operator=(file&& other) -> file& { close(); fd_ = other.fd_; other.fd_ = -1; @@ -286,7 +271,7 @@ class FMT_API file { ~file() noexcept; // Returns the file descriptor. - auto descriptor() const noexcept -> int { return fd_; } + inline auto descriptor() const noexcept -> int { return fd_; } // Closes the file. void close(); @@ -313,11 +298,6 @@ class FMT_API file { // necessary. void dup2(int fd, std::error_code& ec) noexcept; - // Creates a pipe setting up read_end and write_end file objects for reading - // and writing respectively. - // DEPRECATED! Taking files as out parameters is deprecated. - static void pipe(file& read_end, file& write_end); - // Creates a buffered_file object associated with this file and detaches // this file object from the file. auto fdopen(const char* mode) -> buffered_file; @@ -329,15 +309,24 @@ class FMT_API file { # endif }; +struct FMT_API pipe { + file read_end; + file write_end; + + // Creates a pipe setting up read_end and write_end file objects for reading + // and writing respectively. + pipe(); +}; + // Returns the memory page size. auto getpagesize() -> long; namespace detail { struct buffer_size { - buffer_size() = default; + constexpr buffer_size() = default; size_t value = 0; - auto operator=(size_t val) const -> buffer_size { + FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { auto bs = buffer_size(); bs.value = val; return bs; @@ -348,7 +337,7 @@ struct ostream_params { int oflag = file::WRONLY | file::CREATE | file::TRUNC; size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; - ostream_params() {} + constexpr ostream_params() {} template ostream_params(T... params, int new_oflag) : ostream_params(params...) { @@ -369,79 +358,62 @@ struct ostream_params { # endif }; -class file_buffer final : public buffer { - file file_; - - FMT_API void grow(size_t) override; - - public: - FMT_API file_buffer(cstring_view path, const ostream_params& params); - FMT_API file_buffer(file_buffer&& other); - FMT_API ~file_buffer(); - - void flush() { - if (size() == 0) return; - file_.write(data(), size() * sizeof(data()[0])); - clear(); - } - - void close() { - flush(); - file_.close(); - } -}; - } // namespace detail -// Added {} below to work around default constructor error known to -// occur in Xcode versions 7.2.1 and 8.2.1. -constexpr detail::buffer_size buffer_size{}; +FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); -/** A fast output stream which is not thread-safe. */ -class FMT_API ostream { +/// A fast buffered output stream for writing from a single thread. Writing from +/// multiple threads without external synchronization may result in a data race. +class FMT_API ostream : private detail::buffer { private: - FMT_MSC_WARNING(suppress : 4251) - detail::file_buffer buffer_; + file file_; - ostream(cstring_view path, const detail::ostream_params& params) - : buffer_(path, params) {} + ostream(cstring_view path, const detail::ostream_params& params); - public: - ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {} + static void grow(buffer& buf, size_t); + public: + ostream(ostream&& other) noexcept; ~ostream(); - void flush() { buffer_.flush(); } + operator writer() { + detail::buffer& buf = *this; + return buf; + } + + inline void flush() { + if (size() == 0) return; + file_.write(data(), size() * sizeof(data()[0])); + clear(); + } template friend auto output_file(cstring_view path, T... params) -> ostream; - void close() { buffer_.close(); } + inline void close() { + flush(); + file_.close(); + } - /** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file. - */ + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. template void print(format_string fmt, T&&... args) { - vformat_to(std::back_inserter(buffer_), fmt, - fmt::make_format_args(args...)); + vformat_to(appender(*this), fmt.str, vargs{{args...}}); } }; /** - \rst - Opens a file for writing. Supported parameters passed in *params*: - - * ````: Flags passed to `open - `_ - (``file::WRONLY | file::CREATE | file::TRUNC`` by default) - * ``buffer_size=``: Output buffer size - - **Example**:: - - auto out = fmt::output_file("guide.txt"); - out.print("Don't {}", "Panic"); - \endrst + * Opens a file for writing. Supported parameters passed in `params`: + * + * - ``: Flags passed to [open]( + * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) + * (`file::WRONLY | file::CREATE | file::TRUNC` by default) + * - `buffer_size=`: Output buffer size + * + * **Example**: + * + * auto out = fmt::output_file("guide.txt"); + * out.print("Don't {}", "Panic"); */ template inline auto output_file(cstring_view path, T... params) -> ostream { diff --git a/deps/spdlog/include/spdlog/fmt/bundled/ostream.h b/deps/spdlog/include/spdlog/fmt/bundled/ostream.h index 26fb3b5ac07..7bec4efe2b3 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/ostream.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/ostream.h @@ -8,7 +8,9 @@ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ -#include // std::filebuf +#ifndef FMT_MODULE +# include // std::filebuf +#endif #ifdef _WIN32 # ifdef __GLIBCXX__ @@ -18,42 +20,19 @@ # include #endif -#include "format.h" +#include "chrono.h" // formatbuf + +#ifdef _MSVC_STL_UPDATE +# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE +#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5 +# define FMT_MSVC_STL_UPDATE _MSVC_LANG +#else +# define FMT_MSVC_STL_UPDATE 0 +#endif FMT_BEGIN_NAMESPACE namespace detail { -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const char_type* s, streamsize count) -> streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - // Generate a unique explicit instantion in every translation unit using a tag // type in an anonymous namespace. namespace { @@ -64,53 +43,18 @@ class file_access { friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } }; -#if FMT_MSC_VERSION +#if FMT_MSVC_STL_UPDATE template class file_access; auto get_file(std::filebuf&) -> FILE*; #endif -inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) - -> bool { - FILE* f = nullptr; -#if FMT_MSC_VERSION - if (auto* buf = dynamic_cast(os.rdbuf())) - f = get_file(*buf); - else - return false; -#elif defined(_WIN32) && defined(__GLIBCXX__) - auto* rdbuf = os.rdbuf(); - if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) - f = sfbuf->file(); - else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) - f = fbuf->file(); - else - return false; -#else - ignore_unused(os, data, f); -#endif -#ifdef _WIN32 - if (f) { - int fd = _fileno(f); - if (_isatty(fd)) { - os.flush(); - return write_console(fd, data); - } - } -#endif - return false; -} -inline auto write_ostream_unicode(std::wostream&, - fmt::basic_string_view) -> bool { - return false; -} - // Write the content of buf to os. // It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { const Char* buf_data = buf.data(); - using unsigned_streamsize = std::make_unsigned::type; + using unsigned_streamsize = make_unsigned_t; unsigned_streamsize size = buf.size(); unsigned_streamsize max_size = to_unsigned(max_value()); do { @@ -121,21 +65,9 @@ void write_buffer(std::basic_ostream& os, buffer& buf) { } while (size != 0); } -template -void format_value(buffer& buf, const T& value) { - auto&& format_buf = formatbuf>(buf); - auto&& output = std::basic_ostream(&format_buf); -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) - output.imbue(std::locale::classic()); // The default is always unlocalized. -#endif - output << value; - output.exceptions(std::ios_base::failbit | std::ios_base::badbit); -} - template struct streamed_view { const T& value; }; - } // namespace detail // Formats an object of type T that has an overloaded ostream operator<<. @@ -143,11 +75,14 @@ template struct basic_ostream_formatter : formatter, Char> { void set_debug_format() = delete; - template - auto format(const T& value, basic_format_context& ctx) const - -> OutputIt { + template + auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { auto buffer = basic_memory_buffer(); - detail::format_value(buffer, value); + auto&& formatbuf = detail::formatbuf>(buffer); + auto&& output = std::basic_ostream(&formatbuf); + output.imbue(std::locale::classic()); // The default is always unlocalized. + output << value; + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } @@ -158,86 +93,73 @@ using ostream_formatter = basic_ostream_formatter; template struct formatter, Char> : basic_ostream_formatter { - template - auto format(detail::streamed_view view, - basic_format_context& ctx) const -> OutputIt { + template + auto format(detail::streamed_view view, Context& ctx) const + -> decltype(ctx.out()) { return basic_ostream_formatter::format(view.value, ctx); } }; /** - \rst - Returns a view that formats `value` via an ostream ``operator<<``. - - **Example**:: - - fmt::print("Current thread id: {}\n", - fmt::streamed(std::this_thread::get_id())); - \endrst + * Returns a view that formats `value` via an ostream `operator<<`. + * + * **Example**: + * + * fmt::print("Current thread id: {}\n", + * fmt::streamed(std::this_thread::get_id())); */ template constexpr auto streamed(const T& value) -> detail::streamed_view { return {value}; } -namespace detail { - -inline void vprint_directly(std::ostream& os, string_view format_str, - format_args args) { +inline void vprint(std::ostream& os, string_view fmt, format_args args) { auto buffer = memory_buffer(); - detail::vformat_to(buffer, format_str, args); - detail::write_buffer(os, buffer); -} - -} // namespace detail - -FMT_EXPORT template -void vprint(std::basic_ostream& os, - basic_string_view> format_str, - basic_format_args>> args) { - auto buffer = basic_memory_buffer(); - detail::vformat_to(buffer, format_str, args); - if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; + detail::vformat_to(buffer, fmt, args); + FILE* f = nullptr; +#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI + if (auto* buf = dynamic_cast(os.rdbuf())) + f = detail::get_file(*buf); +#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI + auto* rdbuf = os.rdbuf(); + if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) + f = sfbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) + f = fbuf->file(); +#endif +#ifdef _WIN32 + if (f) { + int fd = _fileno(f); + if (_isatty(fd)) { + os.flush(); + if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; + } + } +#endif + detail::ignore_unused(f); detail::write_buffer(os, buffer); } /** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fmt::print(cerr, "Don't {}!", "panic"); - \endrst + * Prints formatted data to the stream `os`. + * + * **Example**: + * + * fmt::print(cerr, "Don't {}!", "panic"); */ FMT_EXPORT template void print(std::ostream& os, format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - if (detail::is_utf8()) - vprint(os, fmt, vargs); - else - detail::vprint_directly(os, fmt, vargs); -} - -FMT_EXPORT -template -void print(std::wostream& os, - basic_format_string...> fmt, - Args&&... args) { - vprint(os, fmt, fmt::make_format_args>(args...)); + fmt::vargs vargs = {{args...}}; + if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs); + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt.str, vargs); + detail::write_buffer(os, buffer); } FMT_EXPORT template void println(std::ostream& os, format_string fmt, T&&... args) { - fmt::print(os, "{}\n", fmt::format(fmt, std::forward(args)...)); -} - -FMT_EXPORT -template -void println(std::wostream& os, - basic_format_string...> fmt, - Args&&... args) { - print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); + fmt::print(os, FMT_STRING("{}\n"), + fmt::format(fmt, std::forward(args)...)); } FMT_END_NAMESPACE diff --git a/deps/spdlog/include/spdlog/fmt/bundled/printf.h b/deps/spdlog/include/spdlog/fmt/bundled/printf.h index 07e81577cf3..e7268401854 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/printf.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/printf.h @@ -8,8 +8,10 @@ #ifndef FMT_PRINTF_H_ #define FMT_PRINTF_H_ -#include // std::max -#include // std::numeric_limits +#ifndef FMT_MODULE +# include // std::max +# include // std::numeric_limits +#endif #include "format.h" @@ -22,7 +24,7 @@ template struct printf_formatter { template class basic_printf_context { private: - detail::buffer_appender out_; + basic_appender out_; basic_format_args args_; static_assert(std::is_same::value || @@ -31,43 +33,53 @@ template class basic_printf_context { public: using char_type = Char; - using parse_context_type = basic_format_parse_context; + using parse_context_type = parse_context; template using formatter_type = printf_formatter; + enum { builtin_types = 1 }; - /** - \rst - Constructs a ``printf_context`` object. References to the arguments are - stored in the context object so make sure they have appropriate lifetimes. - \endrst - */ - basic_printf_context(detail::buffer_appender out, + /// Constructs a `printf_context` object. References to the arguments are + /// stored in the context object so make sure they have appropriate lifetimes. + basic_printf_context(basic_appender out, basic_format_args args) : out_(out), args_(args) {} - auto out() -> detail::buffer_appender { return out_; } - void advance_to(detail::buffer_appender) {} + auto out() -> basic_appender { return out_; } + void advance_to(basic_appender) {} auto locale() -> detail::locale_ref { return {}; } auto arg(int id) const -> basic_format_arg { return args_.get(id); } - - FMT_CONSTEXPR void on_error(const char* message) { - detail::error_handler().on_error(message); - } }; namespace detail { +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = + static_cast(memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct int_checker { template static auto fits_in_int(T value) -> bool { - unsigned max = max_value(); + unsigned max = to_unsigned(max_value()); return value <= max; } - static auto fits_in_int(bool) -> bool { return true; } + inline static auto fits_in_int(bool) -> bool { return true; } }; template <> struct int_checker { @@ -75,20 +87,20 @@ template <> struct int_checker { return value >= (std::numeric_limits::min)() && value <= max_value(); } - static auto fits_in_int(int) -> bool { return true; } + inline static auto fits_in_int(int) -> bool { return true; } }; struct printf_precision_handler { template ::value)> auto operator()(T value) -> int { if (!int_checker::is_signed>::fits_in_int(value)) - throw_format_error("number is too big"); + report_error("number is too big"); return (std::max)(static_cast(value), 0); } template ::value)> auto operator()(T) -> int { - throw_format_error("precision is not integer"); + report_error("precision is not integer"); return 0; } }; @@ -133,25 +145,19 @@ template class arg_converter { using target_type = conditional_t::value, U, T>; if (const_check(sizeof(target_type) <= sizeof(int))) { // Extra casts are used to silence warnings. - if (is_signed) { - auto n = static_cast(static_cast(value)); - arg_ = detail::make_arg(n); - } else { - using unsigned_type = typename make_unsigned_or_bool::type; - auto n = static_cast(static_cast(value)); - arg_ = detail::make_arg(n); - } + using unsigned_type = typename make_unsigned_or_bool::type; + if (is_signed) + arg_ = static_cast(static_cast(value)); + else + arg_ = static_cast(static_cast(value)); } else { - if (is_signed) { - // glibc's printf doesn't sign extend arguments of smaller types: - // std::printf("%lld", -42); // prints "4294967254" - // but we don't have to do the same because it's a UB. - auto n = static_cast(value); - arg_ = detail::make_arg(n); - } else { - auto n = static_cast::type>(value); - arg_ = detail::make_arg(n); - } + // glibc's printf doesn't sign extend arguments of smaller types: + // std::printf("%lld", -42); // prints "4294967254" + // but we don't have to do the same because it's a UB. + if (is_signed) + arg_ = static_cast(value); + else + arg_ = static_cast::type>(value); } } @@ -165,7 +171,7 @@ template class arg_converter { // unsigned). template void convert_arg(basic_format_arg& arg, Char type) { - visit_format_arg(arg_converter(arg, type), arg); + arg.visit(arg_converter(arg, type)); } // Converts an integer argument to char for printf. @@ -178,8 +184,7 @@ template class char_converter { template ::value)> void operator()(T value) { - auto c = static_cast(value); - arg_ = detail::make_arg(c); + arg_ = static_cast(value); } template ::value)> @@ -195,28 +200,28 @@ template struct get_cstring { // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. -template class printf_width_handler { +class printf_width_handler { private: - format_specs& specs_; + format_specs& specs_; public: - explicit printf_width_handler(format_specs& specs) : specs_(specs) {} + inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {} template ::value)> auto operator()(T value) -> unsigned { auto width = static_cast>(value); if (detail::is_negative(value)) { - specs_.align = align::left; + specs_.set_align(align::left); width = 0 - width; } - unsigned int_max = max_value(); - if (width > int_max) throw_format_error("number is too big"); + unsigned int_max = to_unsigned(max_value()); + if (width > int_max) report_error("number is too big"); return static_cast(width); } template ::value)> auto operator()(T) -> unsigned { - throw_format_error("width is not integer"); + report_error("width is not integer"); return 0; } }; @@ -224,12 +229,12 @@ template class printf_width_handler { // Workaround for a bug with the XL compiler when initializing // printf_arg_formatter's base class. template -auto make_arg_formatter(buffer_appender iter, format_specs& s) +auto make_arg_formatter(basic_appender iter, format_specs& s) -> arg_formatter { return {iter, s, locale_ref()}; } -// The ``printf`` argument formatter. +// The `printf` argument formatter. template class printf_arg_formatter : public arg_formatter { private: @@ -240,105 +245,96 @@ class printf_arg_formatter : public arg_formatter { void write_null_pointer(bool is_string = false) { auto s = this->specs; - s.type = presentation_type::none; - write_bytes(this->out, is_string ? "(null)" : "(nil)", s); + s.set_type(presentation_type::none); + write_bytes(this->out, is_string ? "(null)" : "(nil)", s); + } + + template void write(T value) { + detail::write(this->out, value, this->specs, this->locale); } public: - printf_arg_formatter(buffer_appender iter, format_specs& s, + printf_arg_formatter(basic_appender iter, format_specs& s, context_type& ctx) : base(make_arg_formatter(iter, s)), context_(ctx) {} - void operator()(monostate value) { base::operator()(value); } + void operator()(monostate value) { write(value); } template ::value)> void operator()(T value) { // MSVC2013 fails to compile separate overloads for bool and Char so use // std::is_same instead. if (!std::is_same::value) { - base::operator()(value); + write(value); return; } - format_specs fmt_specs = this->specs; - if (fmt_specs.type != presentation_type::none && - fmt_specs.type != presentation_type::chr) { + format_specs s = this->specs; + if (s.type() != presentation_type::none && + s.type() != presentation_type::chr) { return (*this)(static_cast(value)); } - fmt_specs.sign = sign::none; - fmt_specs.alt = false; - fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. + s.set_sign(sign::none); + s.clear_alt(); + s.set_fill(' '); // Ignore '0' flag for char types. // align::numeric needs to be overwritten here since the '0' flag is // ignored for non-numeric types - if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) - fmt_specs.align = align::right; - write(this->out, static_cast(value), fmt_specs); + if (s.align() == align::none || s.align() == align::numeric) + s.set_align(align::right); + detail::write(this->out, static_cast(value), s); } template ::value)> void operator()(T value) { - base::operator()(value); + write(value); } - /** Formats a null-terminated C string. */ void operator()(const char* value) { if (value) - base::operator()(value); + write(value); else - write_null_pointer(this->specs.type != presentation_type::pointer); + write_null_pointer(this->specs.type() != presentation_type::pointer); } - /** Formats a null-terminated wide C string. */ void operator()(const wchar_t* value) { if (value) - base::operator()(value); + write(value); else - write_null_pointer(this->specs.type != presentation_type::pointer); + write_null_pointer(this->specs.type() != presentation_type::pointer); } - void operator()(basic_string_view value) { base::operator()(value); } + void operator()(basic_string_view value) { write(value); } - /** Formats a pointer. */ void operator()(const void* value) { if (value) - base::operator()(value); + write(value); else write_null_pointer(); } - /** Formats an argument of a custom (user-defined) type. */ void operator()(typename basic_format_arg::handle handle) { - auto parse_ctx = basic_format_parse_context({}); + auto parse_ctx = parse_context({}); handle.format(parse_ctx, context_); } }; template -void parse_flags(format_specs& specs, const Char*& it, const Char* end) { +void parse_flags(format_specs& specs, const Char*& it, const Char* end) { for (; it != end; ++it) { switch (*it) { - case '-': - specs.align = align::left; - break; - case '+': - specs.sign = sign::plus; - break; - case '0': - specs.fill[0] = '0'; - break; + case '-': specs.set_align(align::left); break; + case '+': specs.set_sign(sign::plus); break; + case '0': specs.set_fill('0'); break; case ' ': - if (specs.sign != sign::plus) specs.sign = sign::space; - break; - case '#': - specs.alt = true; + if (specs.sign() != sign::plus) specs.set_sign(sign::space); break; - default: - return; + case '#': specs.set_alt(); break; + default: return; } } } template -auto parse_header(const Char*& it, const Char* end, format_specs& specs, +auto parse_header(const Char*& it, const Char* end, format_specs& specs, GetArg get_arg) -> int { int arg_index = -1; Char c = *it; @@ -350,11 +346,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs, ++it; arg_index = value != -1 ? value : max_value(); } else { - if (c == '0') specs.fill[0] = '0'; + if (c == '0') specs.set_fill('0'); if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. - if (value == -1) throw_format_error("number is too big"); + if (value == -1) report_error("number is too big"); specs.width = value; return arg_index; } @@ -365,63 +361,47 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs, if (it != end) { if (*it >= '0' && *it <= '9') { specs.width = parse_nonnegative_int(it, end, -1); - if (specs.width == -1) throw_format_error("number is too big"); + if (specs.width == -1) report_error("number is too big"); } else if (*it == '*') { ++it; - specs.width = static_cast(visit_format_arg( - detail::printf_width_handler(specs), get_arg(-1))); + specs.width = static_cast( + get_arg(-1).visit(detail::printf_width_handler(specs))); } } return arg_index; } -inline auto parse_printf_presentation_type(char c, type t) +inline auto parse_printf_presentation_type(char c, type t, bool& upper) -> presentation_type { using pt = presentation_type; constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; switch (c) { - case 'd': - return in(t, integral_set) ? pt::dec : pt::none; - case 'o': - return in(t, integral_set) ? pt::oct : pt::none; - case 'x': - return in(t, integral_set) ? pt::hex_lower : pt::none; - case 'X': - return in(t, integral_set) ? pt::hex_upper : pt::none; - case 'a': - return in(t, float_set) ? pt::hexfloat_lower : pt::none; - case 'A': - return in(t, float_set) ? pt::hexfloat_upper : pt::none; - case 'e': - return in(t, float_set) ? pt::exp_lower : pt::none; - case 'E': - return in(t, float_set) ? pt::exp_upper : pt::none; - case 'f': - return in(t, float_set) ? pt::fixed_lower : pt::none; - case 'F': - return in(t, float_set) ? pt::fixed_upper : pt::none; - case 'g': - return in(t, float_set) ? pt::general_lower : pt::none; - case 'G': - return in(t, float_set) ? pt::general_upper : pt::none; - case 'c': - return in(t, integral_set) ? pt::chr : pt::none; - case 's': - return in(t, string_set | cstring_set) ? pt::string : pt::none; - case 'p': - return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; - default: - return pt::none; + case 'd': return in(t, integral_set) ? pt::dec : pt::none; + case 'o': return in(t, integral_set) ? pt::oct : pt::none; + case 'X': upper = true; FMT_FALLTHROUGH; + case 'x': return in(t, integral_set) ? pt::hex : pt::none; + case 'E': upper = true; FMT_FALLTHROUGH; + case 'e': return in(t, float_set) ? pt::exp : pt::none; + case 'F': upper = true; FMT_FALLTHROUGH; + case 'f': return in(t, float_set) ? pt::fixed : pt::none; + case 'G': upper = true; FMT_FALLTHROUGH; + case 'g': return in(t, float_set) ? pt::general : pt::none; + case 'A': upper = true; FMT_FALLTHROUGH; + case 'a': return in(t, float_set) ? pt::hexfloat : pt::none; + case 'c': return in(t, integral_set) ? pt::chr : pt::none; + case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none; + case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; + default: return pt::none; } } template void vprintf(buffer& buf, basic_string_view format, basic_format_args args) { - using iterator = buffer_appender; + using iterator = basic_appender; auto out = iterator(buf); auto context = basic_printf_context(out, args); - auto parse_ctx = basic_format_parse_context(format); + auto parse_ctx = parse_context(format); // Returns the argument with specified index or, if arg_index is -1, the next // argument. @@ -449,12 +429,12 @@ void vprintf(buffer& buf, basic_string_view format, } write(out, basic_string_view(start, to_unsigned(it - 1 - start))); - auto specs = format_specs(); - specs.align = align::right; + auto specs = format_specs(); + specs.set_align(align::right); // Parse argument index, flags and width. int arg_index = parse_header(it, end, specs, get_arg); - if (arg_index == 0) throw_format_error("argument not found"); + if (arg_index == 0) report_error("argument not found"); // Parse precision. if (it != end && *it == '.') { @@ -464,8 +444,8 @@ void vprintf(buffer& buf, basic_string_view format, specs.precision = parse_nonnegative_int(it, end, 0); } else if (c == '*') { ++it; - specs.precision = static_cast( - visit_format_arg(printf_precision_handler(), get_arg(-1))); + specs.precision = + static_cast(get_arg(-1).visit(printf_precision_handler())); } else { specs.precision = 0; } @@ -474,25 +454,26 @@ void vprintf(buffer& buf, basic_string_view format, auto arg = get_arg(arg_index); // For d, i, o, u, x, and X conversion specifiers, if a precision is // specified, the '0' flag is ignored - if (specs.precision >= 0 && arg.is_integral()) { + if (specs.precision >= 0 && is_integral_type(arg.type())) { // Ignore '0' for non-numeric types or if '-' present. - specs.fill[0] = ' '; + specs.set_fill(' '); } if (specs.precision >= 0 && arg.type() == type::cstring_type) { - auto str = visit_format_arg(get_cstring(), arg); + auto str = arg.visit(get_cstring()); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); auto sv = basic_string_view( str, to_unsigned(nul != str_end ? nul - str : specs.precision)); - arg = make_arg>(sv); + arg = sv; } - if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; - if (specs.fill[0] == '0') { - if (arg.is_arithmetic() && specs.align != align::left) - specs.align = align::numeric; - else - specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' - // flag is also present. + if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt(); + if (specs.fill_unit() == '0') { + if (is_arithmetic_type(arg.type()) && specs.align() != align::left) { + specs.set_align(align::numeric); + } else { + // Ignore '0' flag for non-numeric types or if '-' flag is also present. + specs.set_fill(' '); + } } // Parse length and convert the argument to the required type. @@ -517,47 +498,39 @@ void vprintf(buffer& buf, basic_string_view format, convert_arg(arg, t); } break; - case 'j': - convert_arg(arg, t); - break; - case 'z': - convert_arg(arg, t); - break; - case 't': - convert_arg(arg, t); - break; + case 'j': convert_arg(arg, t); break; + case 'z': convert_arg(arg, t); break; + case 't': convert_arg(arg, t); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; - default: - --it; - convert_arg(arg, c); + default: --it; convert_arg(arg, c); } // Parse type. - if (it == end) throw_format_error("invalid format string"); + if (it == end) report_error("invalid format string"); char type = static_cast(*it++); - if (arg.is_integral()) { + if (is_integral_type(arg.type())) { // Normalize type. switch (type) { case 'i': - case 'u': - type = 'd'; - break; + case 'u': type = 'd'; break; case 'c': - visit_format_arg(char_converter>(arg), arg); + arg.visit(char_converter>(arg)); break; } } - specs.type = parse_printf_presentation_type(type, arg.type()); - if (specs.type == presentation_type::none) - throw_format_error("invalid format specifier"); + bool upper = false; + specs.set_type(parse_printf_presentation_type(type, arg.type(), upper)); + if (specs.type() == presentation_type::none) + report_error("invalid format specifier"); + if (upper) specs.set_upper(); start = it; // Format argument. - visit_format_arg(printf_arg_formatter(out, specs, context), arg); + arg.visit(printf_arg_formatter(out, specs, context)); } write(out, basic_string_view(start, to_unsigned(it - start))); } @@ -569,56 +542,44 @@ using wprintf_context = basic_printf_context; using printf_args = basic_format_args; using wprintf_args = basic_format_args; -/** - \rst - Constructs an `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::printf_args`. - \endrst - */ -template -inline auto make_printf_args(const T&... args) - -> format_arg_store { - return {args...}; +/// Constructs an `format_arg_store` object that contains references to +/// arguments and can be implicitly converted to `printf_args`. +template +inline auto make_printf_args(T&... args) + -> decltype(fmt::make_format_args>(args...)) { + return fmt::make_format_args>(args...); } -// DEPRECATED! -template -inline auto make_wprintf_args(const T&... args) - -> format_arg_store { - return {args...}; -} +template struct vprintf_args { + using type = basic_format_args>; +}; template -inline auto vsprintf( - basic_string_view fmt, - basic_format_args>> args) +inline auto vsprintf(basic_string_view fmt, + typename vprintf_args::type args) -> std::basic_string { auto buf = basic_memory_buffer(); detail::vprintf(buf, fmt, args); - return to_string(buf); + return {buf.data(), buf.size()}; } /** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ -template ::value, char_t>> + * Formats `args` according to specifications in `fmt` and returns the result + * as as string. + * + * **Example**: + * + * std::string message = fmt::sprintf("The answer is %d", 42); + */ +template > inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { return vsprintf(detail::to_string_view(fmt), fmt::make_format_args>(args...)); } template -inline auto vfprintf( - std::FILE* f, basic_string_view fmt, - basic_format_args>> args) - -> int { +inline auto vfprintf(std::FILE* f, basic_string_view fmt, + typename vprintf_args::type args) -> int { auto buf = basic_memory_buffer(); detail::vprintf(buf, fmt, args); size_t size = buf.size(); @@ -628,36 +589,33 @@ inline auto vfprintf( } /** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst + * Formats `args` according to specifications in `fmt` and writes the output + * to `f`. + * + * **Example**: + * + * fmt::fprintf(stderr, "Don't %s!", "panic"); */ -template > +template > inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { return vfprintf(f, detail::to_string_view(fmt), - fmt::make_format_args>(args...)); + make_printf_args(args...)); } template -FMT_DEPRECATED inline auto vprintf( - basic_string_view fmt, - basic_format_args>> args) +FMT_DEPRECATED inline auto vprintf(basic_string_view fmt, + typename vprintf_args::type args) -> int { return vfprintf(stdout, fmt, args); } /** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::printf("Elapsed time: %.2f seconds", 1.23); */ template inline auto printf(string_view fmt, const T&... args) -> int { @@ -666,7 +624,7 @@ inline auto printf(string_view fmt, const T&... args) -> int { template FMT_DEPRECATED inline auto printf(basic_string_view fmt, const T&... args) -> int { - return vfprintf(stdout, fmt, make_wprintf_args(args...)); + return vfprintf(stdout, fmt, make_printf_args(args...)); } FMT_END_EXPORT diff --git a/deps/spdlog/include/spdlog/fmt/bundled/ranges.h b/deps/spdlog/include/spdlog/fmt/bundled/ranges.h index 3638fffb83b..23ff7de0913 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/ranges.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/ranges.h @@ -8,67 +8,31 @@ #ifndef FMT_RANGES_H_ #define FMT_RANGES_H_ -#include -#include -#include +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +#endif #include "format.h" FMT_BEGIN_NAMESPACE -namespace detail { - -template -auto copy(const Range& range, OutputIt out) -> OutputIt { - for (auto it = range.begin(), end = range.end(); it != end; ++it) - *out++ = *it; - return out; -} - -template -auto copy(const char* str, OutputIt out) -> OutputIt { - while (*str) *out++ = *str++; - return out; -} - -template auto copy(char ch, OutputIt out) -> OutputIt { - *out++ = ch; - return out; -} - -template auto copy(wchar_t ch, OutputIt out) -> OutputIt { - *out++ = ch; - return out; -} - -// Returns true if T has a std::string-like interface, like std::string_view. -template class is_std_string_like { - template - static auto check(U* p) - -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); - template static void check(...); - - public: - static constexpr const bool value = - is_string::value || - std::is_convertible>::value || - !std::is_void(nullptr))>::value; -}; +FMT_EXPORT +enum class range_format { disabled, map, set, sequence, string, debug_string }; -template -struct is_std_string_like> : std::true_type {}; +namespace detail { template class is_map { template static auto check(U*) -> typename U::mapped_type; template static void check(...); public: -#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED! - static constexpr const bool value = false; -#else static constexpr const bool value = !std::is_void(nullptr))>::value; -#endif }; template class is_set { @@ -76,26 +40,10 @@ template class is_set { template static void check(...); public: -#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED! - static constexpr const bool value = false; -#else static constexpr const bool value = !std::is_void(nullptr))>::value && !is_map::value; -#endif }; -template struct conditional_helper {}; - -template struct is_range_ : std::false_type {}; - -#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 - -# define FMT_DECLTYPE_RETURN(val) \ - ->decltype(val) { return val; } \ - static_assert( \ - true, "") // This makes it so that a semicolon is required after the - // macro, which helps clang-format handle the formatting. - // C array overload template auto range_begin(const T (&arr)[N]) -> const T* { @@ -110,17 +58,21 @@ template struct has_member_fn_begin_end_t : std::false_type {}; template -struct has_member_fn_begin_end_t().begin()), +struct has_member_fn_begin_end_t().begin()), decltype(std::declval().end())>> : std::true_type {}; -// Member function overload +// Member function overloads. template -auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).begin()); +auto range_begin(T&& rng) -> decltype(static_cast(rng).begin()) { + return static_cast(rng).begin(); +} template -auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).end()); +auto range_end(T&& rng) -> decltype(static_cast(rng).end()) { + return static_cast(rng).end(); +} -// ADL overload. Only participates in overload resolution if member functions +// ADL overloads. Only participate in overload resolution if member functions // are not found. template auto range_begin(T&& rng) @@ -141,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {}; template struct has_const_begin_end< - T, - void_t< - decltype(detail::range_begin(std::declval&>())), - decltype(detail::range_end(std::declval&>()))>> + T, void_t&>())), + decltype(detail::range_end( + std::declval&>()))>> : std::true_type {}; template struct has_mutable_begin_end< - T, void_t())), - decltype(detail::range_end(std::declval())), + T, void_t())), + decltype(detail::range_end(std::declval())), // the extra int here is because older versions of MSVC don't // SFINAE properly unless there are distinct types int>> : std::true_type {}; +template struct is_range_ : std::false_type {}; template struct is_range_ : std::integral_constant::value || has_mutable_begin_end::value)> {}; -# undef FMT_DECLTYPE_RETURN -#endif // tuple_size and tuple_element check. template class is_tuple_like_ { - template - static auto check(U* p) -> decltype(std::tuple_size::value, int()); + template ::type> + static auto check(U* p) -> decltype(std::tuple_size::value, 0); template static void check(...); public: @@ -206,12 +157,13 @@ class is_tuple_formattable_ { static constexpr const bool value = false; }; template class is_tuple_formattable_ { - template - static auto check2(index_sequence, - integer_sequence) -> std::true_type; - static auto check2(...) -> std::false_type; - template - static auto check(index_sequence) -> decltype(check2( + template + static auto all_true(index_sequence, + integer_sequence= 0)...>) -> std::true_type; + static auto all_true(...) -> std::false_type; + + template + static auto check(index_sequence) -> decltype(all_true( index_sequence{}, integer_sequence::type, @@ -292,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) template FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} +template +struct range_format_kind_ + : std::integral_constant, T>::value + ? range_format::disabled + : is_map::value ? range_format::map + : is_set::value ? range_format::set + : range_format::sequence> {}; + +template +using range_format_constant = std::integral_constant; + // These are not generic lambdas for compatibility with C++11. -template struct parse_empty_specs { +template struct parse_empty_specs { template FMT_CONSTEXPR void operator()(Formatter& f) { f.parse(ctx); detail::maybe_set_debug_format(f, true); } - ParseContext& ctx; + parse_context& ctx; }; template struct format_tuple_element { using char_type = typename FormatContext::char_type; template void operator()(const formatter& f, const T& v) { - if (i > 0) - ctx.advance_to(detail::copy_str(separator, ctx.out())); + if (i > 0) ctx.advance_to(detail::copy(separator, ctx.out())); ctx.advance_to(f.format(v, ctx)); ++i; } @@ -355,66 +318,48 @@ struct formatter - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); - if (it != ctx.end() && *it != '}') - FMT_THROW(format_error("invalid format specifier")); - detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + auto end = ctx.end(); + if (it != end && detail::to_ascii(*it) == 'n') { + ++it; + set_brackets({}, {}); + set_separator({}); + } + if (it != end && *it != '}') report_error("invalid format specifier"); + ctx.advance_to(it); + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); return it; } template auto format(const Tuple& value, FormatContext& ctx) const -> decltype(ctx.out()) { - ctx.advance_to(detail::copy_str(opening_bracket_, ctx.out())); + ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); detail::for_each2( formatters_, value, detail::format_tuple_element{0, ctx, separator_}); - return detail::copy_str(closing_bracket_, ctx.out()); + return detail::copy(closing_bracket_, ctx.out()); } }; template struct is_range { static constexpr const bool value = - detail::is_range_::value && !detail::is_std_string_like::value && - !std::is_convertible>::value && - !std::is_convertible>::value; + detail::is_range_::value && !detail::has_to_string_view::value; }; namespace detail { -template struct range_mapper { - using mapper = arg_mapper; - - template , Context>::value)> - static auto map(T&& value) -> T&& { - return static_cast(value); - } - template , Context>::value)> - static auto map(T&& value) - -> decltype(mapper().map(static_cast(value))) { - return mapper().map(static_cast(value)); - } -}; template -using range_formatter_type = - formatter>{}.map( - std::declval()))>, - Char>; +using range_formatter_type = formatter, Char>; template using maybe_const_range = conditional_t::value, const R, R>; -// Workaround a bug in MSVC 2015 and earlier. -#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 template struct is_formattable_delayed : is_formattable>, Char> {}; -#endif } // namespace detail template struct conjunction : std::true_type {}; @@ -438,6 +383,24 @@ struct range_formatter< detail::string_literal{}; basic_string_view closing_bracket_ = detail::string_literal{}; + bool is_debug = false; + + template ::value)> + auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { + auto buf = basic_memory_buffer(); + for (; it != end; ++it) buf.push_back(*it); + auto specs = format_specs(); + specs.set_type(presentation_type::debug); + return detail::write( + out, basic_string_view(buf.data(), buf.size()), specs); + } + + template ::value)> + auto write_debug_string(Output& out, It, Sentinel) const -> Output { + return out; + } public: FMT_CONSTEXPR range_formatter() {} @@ -456,21 +419,40 @@ struct range_formatter< closing_bracket_ = close; } - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); + detail::maybe_set_debug_format(underlying_, true); + if (it == end) return underlying_.parse(ctx); - if (it != end && *it == 'n') { + switch (detail::to_ascii(*it)) { + case 'n': set_brackets({}, {}); ++it; + break; + case '?': + is_debug = true; + set_brackets({}, {}); + ++it; + if (it == end || *it != 's') report_error("invalid format specifier"); + FMT_FALLTHROUGH; + case 's': + if (!std::is_same::value) + report_error("invalid format specifier"); + if (!is_debug) { + set_brackets(detail::string_literal{}, + detail::string_literal{}); + set_separator({}); + detail::maybe_set_debug_format(underlying_, false); + } + ++it; + return it; } if (it != end && *it != '}') { - if (*it != ':') FMT_THROW(format_error("invalid format specifier")); + if (*it != ':') report_error("invalid format specifier"); + detail::maybe_set_debug_format(underlying_, false); ++it; - } else { - detail::maybe_set_debug_format(underlying_, true); } ctx.advance_to(it); @@ -479,106 +461,220 @@ struct range_formatter< template auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::range_mapper> mapper; auto out = ctx.out(); - out = detail::copy_str(opening_bracket_, out); - int i = 0; auto it = detail::range_begin(range); auto end = detail::range_end(range); + if (is_debug) return write_debug_string(out, std::move(it), end); + + out = detail::copy(opening_bracket_, out); + int i = 0; for (; it != end; ++it) { - if (i > 0) out = detail::copy_str(separator_, out); + if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); - auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + auto&& item = *it; // Need an lvalue + out = underlying_.format(item, ctx); ++i; } - out = detail::copy_str(closing_bracket_, out); + out = detail::copy(closing_bracket_, out); return out; } }; -enum class range_format { disabled, map, set, sequence, string, debug_string }; +FMT_EXPORT +template +struct range_format_kind + : conditional_t< + is_range::value, detail::range_format_kind_, + std::integral_constant> {}; -namespace detail { -template -struct range_format_kind_ - : std::integral_constant, T>::value - ? range_format::disabled - : is_map::value ? range_format::map - : is_set::value ? range_format::set - : range_format::sequence> {}; +template +struct formatter< + R, Char, + enable_if_t::value != range_format::disabled && + range_format_kind::value != range_format::map && + range_format_kind::value != range_format::string && + range_format_kind::value != range_format::debug_string>, + detail::is_formattable_delayed>::value>> { + private: + using range_type = detail::maybe_const_range; + range_formatter, Char> range_formatter_; -template -struct range_default_formatter; + public: + using nonlocking = void; + + FMT_CONSTEXPR formatter() { + if (detail::const_check(range_format_kind::value != + range_format::set)) + return; + range_formatter_.set_brackets(detail::string_literal{}, + detail::string_literal{}); + } -template -using range_format_constant = std::integral_constant; + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return range_formatter_.parse(ctx); + } -template -struct range_default_formatter< - K, R, Char, - enable_if_t<(K == range_format::sequence || K == range_format::map || - K == range_format::set)>> { - using range_type = detail::maybe_const_range; - range_formatter, Char> underlying_; + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + return range_formatter_.format(range, ctx); + } +}; + +// A map formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::map>, + detail::is_formattable_delayed>::value>> { + private: + using map_type = detail::maybe_const_range; + using element_type = detail::uncvref_type; - FMT_CONSTEXPR range_default_formatter() { init(range_format_constant()); } + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; + bool no_delimiters_ = false; - FMT_CONSTEXPR void init(range_format_constant) { - underlying_.set_brackets(detail::string_literal{}, - detail::string_literal{}); + public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it != end) { + if (detail::to_ascii(*it) == 'n') { + no_delimiters_ = true; + ++it; + } + if (it != end && *it != '}') { + if (*it != ':') report_error("invalid format specifier"); + ++it; + } + ctx.advance_to(it); + } + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + return it; } - FMT_CONSTEXPR void init(range_format_constant) { - underlying_.set_brackets(detail::string_literal{}, - detail::string_literal{}); - underlying_.underlying().set_brackets({}, {}); - underlying_.underlying().set_separator( - detail::string_literal{}); + template + auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { + auto out = ctx.out(); + basic_string_view open = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(open, out); + int i = 0; + basic_string_view sep = detail::string_literal{}; + for (auto&& value : map) { + if (i > 0) out = detail::copy(sep, out); + ctx.advance_to(out); + detail::for_each2(formatters_, value, + detail::format_tuple_element{ + 0, ctx, detail::string_literal{}}); + ++i; + } + basic_string_view close = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(close, out); + return out; } +}; + +// A (debug_)string formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::string || + range_format_kind::value == + range_format::debug_string>> { + private: + using range_type = detail::maybe_const_range; + using string_type = + conditional_t, + decltype(detail::range_begin(std::declval())), + decltype(detail::range_end(std::declval()))>::value, + detail::std_string_view, std::basic_string>; - FMT_CONSTEXPR void init(range_format_constant) {} + formatter underlying_; - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return underlying_.parse(ctx); } template auto format(range_type& range, FormatContext& ctx) const -> decltype(ctx.out()) { - return underlying_.format(range, ctx); + auto out = ctx.out(); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + out = underlying_.format( + string_type{detail::range_begin(range), detail::range_end(range)}, ctx); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + return out; } }; -} // namespace detail -template -struct range_format_kind - : conditional_t< - is_range::value, detail::range_format_kind_, - std::integral_constant> {}; +template +struct join_view : detail::view { + It begin; + Sentinel end; + basic_string_view sep; -template -struct formatter< - R, Char, - enable_if_t::value != - range_format::disabled> -// Workaround a bug in MSVC 2015 and earlier. -#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 - , - detail::is_formattable_delayed + join_view(It b, Sentinel e, basic_string_view s) + : begin(std::move(b)), end(e), sep(s) {} +}; + +template +struct formatter, Char> { + private: + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t; +#else + typename std::iterator_traits::value_type; #endif - >::value>> - : detail::range_default_formatter::value, R, - Char> { + formatter, Char> value_formatter_; + + using view = conditional_t::value, + const join_view, + join_view>; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return value_formatter_.parse(ctx); + } + + template + auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { + using iter = + conditional_t::value, It, It&>; + iter it = value.begin; + auto out = ctx.out(); + if (it == value.end) return out; + out = value_formatter_.format(*it, ctx); + ++it; + while (it != value.end) { + out = detail::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = value_formatter_.format(*it, ctx); + ++it; + } + return out; + } }; -template struct tuple_join_view : detail::view { - const std::tuple& tuple; +template struct tuple_join_view : detail::view { + const Tuple& tuple; basic_string_view sep; - tuple_join_view(const std::tuple& t, basic_string_view s) + tuple_join_view(const Tuple& t, basic_string_view s) : tuple(t), sep{s} {} }; @@ -589,65 +685,64 @@ template struct tuple_join_view : detail::view { # define FMT_TUPLE_JOIN_SPECIFIERS 0 #endif -template -struct formatter, Char> { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return do_parse(ctx, std::integral_constant()); +template +struct formatter, Char, + enable_if_t::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, std::tuple_size()); } template - auto format(const tuple_join_view& value, + auto format(const tuple_join_view& value, FormatContext& ctx) const -> typename FormatContext::iterator { - return do_format(value, ctx, - std::integral_constant()); + return do_format(value, ctx, std::tuple_size()); } private: - std::tuple::type, Char>...> formatters_; + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; - template - FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) - -> decltype(ctx.begin()) { + -> const Char* { return ctx.begin(); } - template - FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + template + FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) - -> decltype(ctx.begin()) { + -> const Char* { auto end = ctx.begin(); #if FMT_TUPLE_JOIN_SPECIFIERS - end = std::get(formatters_).parse(ctx); + end = std::get::value - N>(formatters_).parse(ctx); if (N > 1) { auto end1 = do_parse(ctx, std::integral_constant()); if (end != end1) - FMT_THROW(format_error("incompatible format specs for tuple elements")); + report_error("incompatible format specs for tuple elements"); } #endif return end; } template - auto do_format(const tuple_join_view&, FormatContext& ctx, + auto do_format(const tuple_join_view&, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { return ctx.out(); } template - auto do_format(const tuple_join_view& value, FormatContext& ctx, + auto do_format(const tuple_join_view& value, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { - auto out = std::get(formatters_) - .format(std::get(value.tuple), ctx); - if (N > 1) { - out = std::copy(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - return do_format(value, ctx, std::integral_constant()); - } - return out; + using std::get; + auto out = + std::get::value - N>(formatters_) + .format(get::value - N>(value.tuple), ctx); + if (N <= 1) return out; + out = detail::copy(value.sep, out); + ctx.advance_to(out); + return do_format(value, ctx, std::integral_constant()); } }; @@ -679,52 +774,69 @@ struct formatter< : formatter, Char> { using all = detail::all; template - auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { struct getter : T { - static auto get(const T& t) -> all { - return {t.*(&getter::c)}; // Access c through the derived class. + static auto get(const T& v) -> all { + return {v.*(&getter::c)}; // Access c through the derived class. } }; - return formatter::format(getter::get(t), ctx); + return formatter::format(getter::get(value), ctx); } }; FMT_BEGIN_EXPORT -/** - \rst - Returns an object that formats `tuple` with elements separated by `sep`. - - **Example**:: +/// Returns a view that formats the iterator range `[begin, end)` with elements +/// separated by `sep`. +template +auto join(It begin, Sentinel end, string_view sep) -> join_view { + return {std::move(begin), end, sep}; +} - std::tuple t = {1, 'a'}; - fmt::print("{}", fmt::join(t, ", ")); - // Output: "1, a" - \endrst +/** + * Returns a view that formats `range` with elements separated by `sep`. + * + * **Example**: + * + * auto v = std::vector{1, 2, 3}; + * fmt::print("{}", fmt::join(v, ", ")); + * // Output: 1, 2, 3 + * + * `fmt::join` applies passed format specifiers to the range elements: + * + * fmt::print("{:02}", fmt::join(v, ", ")); + * // Output: 01, 02, 03 */ -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) - -> tuple_join_view { - return {tuple, sep}; +template ::value)> +auto join(Range&& r, string_view sep) + -> join_view { + return {detail::range_begin(r), detail::range_end(r), sep}; } -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, - basic_string_view sep) - -> tuple_join_view { +/** + * Returns an object that formats `std::tuple` with elements separated by `sep`. + * + * **Example**: + * + * auto t = std::tuple{1, 'a'}; + * fmt::print("{}", fmt::join(t, ", ")); + * // Output: 1, a + */ +template ::value)> +FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) + -> tuple_join_view { return {tuple, sep}; } /** - \rst - Returns an object that formats `initializer_list` with elements separated by - `sep`. - - **Example**:: - - fmt::print("{}", fmt::join({1, 2, 3}, ", ")); - // Output: "1, 2, 3" - \endrst + * Returns an object that formats `std::initializer_list` with elements + * separated by `sep`. + * + * **Example**: + * + * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); + * // Output: "1, 2, 3" */ template auto join(std::initializer_list list, string_view sep) diff --git a/deps/spdlog/include/spdlog/fmt/bundled/std.h b/deps/spdlog/include/spdlog/fmt/bundled/std.h index 7cff1159201..f43dc74d21c 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/std.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/std.h @@ -8,38 +8,48 @@ #ifndef FMT_STD_H_ #define FMT_STD_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "format.h" #include "ostream.h" -#if FMT_HAS_INCLUDE() -# include -#endif -// Checking FMT_CPLUSPLUS for warning suppression in MSVC. -#if FMT_CPLUSPLUS >= 201703L -# if FMT_HAS_INCLUDE() -# include +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. +# if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE() && \ + (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif # endif -# if FMT_HAS_INCLUDE() -# include +// Use > instead of >= in the version check because may be +// available after C++17 but before C++20 is marked as implemented. +# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() +# include # endif -# if FMT_HAS_INCLUDE() -# include +# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() +# include # endif -#endif +#endif // FMT_MODULE -#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() -# include +#if FMT_HAS_INCLUDE() +# include #endif // GCC 4 does not support FMT_HAS_INCLUDE. @@ -52,17 +62,6 @@ # endif #endif -// Check if typeid is available. -#ifndef FMT_USE_TYPEID -// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI. -# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \ - defined(__INTEL_RTTI__) || defined(__RTTI) -# define FMT_USE_TYPEID 1 -# else -# define FMT_USE_TYPEID 0 -# endif -#endif - // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. #ifndef FMT_CPP_LIB_FILESYSTEM # ifdef __cpp_lib_filesystem @@ -114,10 +113,9 @@ void write_escaped_path(basic_memory_buffer& quoted, } // namespace detail -FMT_EXPORT template struct formatter { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; char path_type_ = 0; @@ -125,33 +123,33 @@ template struct formatter { public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + FMT_CONSTEXPR auto parse(parse_context& ctx) { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } - if (it != end && (*it == 'g')) path_type_ = *it++; + if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); return it; } template auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; -# ifdef _WIN32 - auto path_string = !path_type_ ? p.native() : p.generic_wstring(); -# else - auto path_string = !path_type_ ? p.native() : p.generic_string(); -# endif + auto path_string = + !path_type_ ? p.native() + : p.generic_string(); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); if (!debug_) { auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); @@ -163,13 +161,29 @@ template struct formatter { specs); } }; + +class path : public std::filesystem::path { + public: + auto display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{}"), base); + } + auto system_string() const -> std::string { return string(); } + + auto generic_display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{:g}"), base); + } + auto generic_system_string() const -> std::string { return generic_string(); } +}; + FMT_END_NAMESPACE #endif // FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE -FMT_EXPORT template -struct formatter, Char> : nested_formatter { +struct formatter, Char> + : nested_formatter, Char> { private: // Functor because C++11 doesn't support generic lambdas. struct writer { @@ -189,18 +203,16 @@ struct formatter, Char> : nested_formatter { template auto format(const std::bitset& bs, FormatContext& ctx) const -> decltype(ctx.out()) { - return write_padded(ctx, writer{bs}); + return this->write_padded(ctx, writer{bs}); } }; -FMT_EXPORT template struct formatter : basic_ostream_formatter {}; FMT_END_NAMESPACE #ifdef __cpp_lib_optional FMT_BEGIN_NAMESPACE -FMT_EXPORT template struct formatter, Char, std::enable_if_t::value>> { @@ -222,7 +234,7 @@ struct formatter, Char, FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} public: - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + FMT_CONSTEXPR auto parse(parse_context& ctx) { maybe_set_debug_format(underlying_, true); return underlying_.parse(ctx); } @@ -242,14 +254,61 @@ struct formatter, Char, FMT_END_NAMESPACE #endif // __cpp_lib_optional -#ifdef __cpp_lib_source_location +#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT + FMT_BEGIN_NAMESPACE -FMT_EXPORT -template <> struct formatter { - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { +namespace detail { + +template +auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (has_to_string_view::value) + return write_escaped_string(out, detail::to_string_view(v)); + if constexpr (std::is_same_v) return write_escaped_char(out, v); + return write(out, v); +} + +} // namespace detail + +FMT_END_NAMESPACE +#endif + +#ifdef __cpp_lib_expected +FMT_BEGIN_NAMESPACE + +template +struct formatter, Char, + std::enable_if_t<(std::is_void::value || + is_formattable::value) && + is_formattable::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } + template + auto format(const std::expected& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + if (value.has_value()) { + out = detail::write(out, "expected("); + if constexpr (!std::is_void::value) + out = detail::write_escaped_alternative(out, *value); + } else { + out = detail::write(out, "unexpected("); + out = detail::write_escaped_alternative(out, value.error()); + } + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_expected + +#ifdef __cpp_lib_source_location +FMT_BEGIN_NAMESPACE +template <> struct formatter { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } + template auto format(const std::source_location& loc, FormatContext& ctx) const -> decltype(ctx.out()) { @@ -291,16 +350,6 @@ template class is_variant_formattable_ { decltype(check(variant_index_sequence{}))::value; }; -template -auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { - if constexpr (is_string::value) - return write_escaped_string(out, detail::to_string_view(v)); - else if constexpr (std::is_same_v) - return write_escaped_char(out, v); - else - return write(out, v); -} - } // namespace detail template struct is_variant_like { @@ -312,10 +361,8 @@ template struct is_variant_formattable { detail::is_variant_formattable_::value; }; -FMT_EXPORT template struct formatter { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } @@ -326,14 +373,12 @@ template struct formatter { } }; -FMT_EXPORT template struct formatter< Variant, Char, std::enable_if_t, is_variant_formattable>>> { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } @@ -346,7 +391,7 @@ struct formatter< FMT_TRY { std::visit( [&](const auto& v) { - out = detail::write_variant_alternative(out, v); + out = detail::write_escaped_alternative(out, v); }, value); } @@ -361,25 +406,145 @@ FMT_END_NAMESPACE #endif // FMT_CPP_LIB_VARIANT FMT_BEGIN_NAMESPACE -FMT_EXPORT -template struct formatter { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); +template <> struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + bool debug_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + + it = detail::parse_align(it, end, specs_); + + char c = *it; + if (it != end && ((c >= '0' && c <= '9') || c == '{')) + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + + if (it != end && *it == '?') { + debug_ = true; + ++it; + } + if (it != end && *it == 's') { + specs_.set_type(presentation_type::string); + ++it; + } + return it; } template - FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const + FMT_CONSTEXPR20 auto format(const std::error_code& ec, + FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + auto buf = memory_buffer(); + if (specs_.type() == presentation_type::string) { + buf.append(ec.message()); + } else { + buf.append(string_view(ec.category().name())); + buf.push_back(':'); + detail::write(appender(buf), ec.value()); + } + auto quoted = memory_buffer(); + auto str = string_view(buf.data(), buf.size()); + if (debug_) { + detail::write_escaped_string(std::back_inserter(quoted), str); + str = string_view(quoted.data(), quoted.size()); + } + return detail::write(ctx.out(), str, specs); + } +}; + +#if FMT_USE_RTTI +namespace detail { + +template +auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { +# ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + std::size_t size = 0; + std::unique_ptr demangled_name_ptr( + abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); + + string_view demangled_name_view; + if (demangled_name_ptr) { + demangled_name_view = demangled_name_ptr.get(); + + // Normalization of stdlib inline namespace names. + // libc++ inline namespaces. + // std::__1::* -> std::* + // std::__1::__fs::* -> std::* + // libstdc++ inline namespaces. + // std::__cxx11::* -> std::* + // std::filesystem::__cxx11::* -> std::filesystem::* + if (demangled_name_view.starts_with("std::")) { + char* begin = demangled_name_ptr.get(); + char* to = begin + 5; // std:: + for (char *from = to, *end = begin + demangled_name_view.size(); + from < end;) { + // This is safe, because demangled_name is NUL-terminated. + if (from[0] == '_' && from[1] == '_') { + char* next = from + 1; + while (next < end && *next != ':') next++; + if (next[0] == ':' && next[1] == ':') { + from = next + 2; + continue; + } + } + *to++ = *from++; + } + demangled_name_view = {begin, detail::to_unsigned(to - begin)}; + } + } else { + demangled_name_view = string_view(ti.name()); + } + return detail::write_bytes(out, demangled_name_view); +# elif FMT_MSC_VERSION + const string_view demangled_name(ti.name()); + for (std::size_t i = 0; i < demangled_name.size(); ++i) { + auto sub = demangled_name; + sub.remove_prefix(i); + if (sub.starts_with("enum ")) { + i += 4; + continue; + } + if (sub.starts_with("class ") || sub.starts_with("union ")) { + i += 5; + continue; + } + if (sub.starts_with("struct ")) { + i += 6; + continue; + } + if (*sub.begin() != ' ') *out++ = *sub.begin(); + } + return out; +# else + return detail::write_bytes(out, string_view(ti.name())); +# endif +} + +} // namespace detail + +template +struct formatter { + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::type_info& ti, Context& ctx) const -> decltype(ctx.out()) { - auto out = ctx.out(); - out = detail::write_bytes(out, ec.category().name(), format_specs()); - out = detail::write(out, Char(':')); - out = detail::write(out, ec.value()); - return out; + return detail::write_demangled_name(ctx.out(), ti); } }; +#endif -FMT_EXPORT template struct formatter< T, Char, // DEPRECATED! Mixing code unit types. @@ -388,81 +553,29 @@ struct formatter< bool with_typename_ = false; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); if (it == end || *it == '}') return it; if (*it == 't') { ++it; - with_typename_ = FMT_USE_TYPEID != 0; + with_typename_ = FMT_USE_RTTI != 0; } return it; } - template - auto format(const std::exception& ex, - basic_format_context& ctx) const -> OutputIt { - format_specs spec; + template + auto format(const std::exception& ex, Context& ctx) const + -> decltype(ctx.out()) { auto out = ctx.out(); - if (!with_typename_) - return detail::write_bytes(out, string_view(ex.what()), spec); - -#if FMT_USE_TYPEID - const std::type_info& ti = typeid(ex); -# ifdef FMT_HAS_ABI_CXA_DEMANGLE - int status = 0; - std::size_t size = 0; - std::unique_ptr demangled_name_ptr( - abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); - - string_view demangled_name_view; - if (demangled_name_ptr) { - demangled_name_view = demangled_name_ptr.get(); - - // Normalization of stdlib inline namespace names. - // libc++ inline namespaces. - // std::__1::* -> std::* - // std::__1::__fs::* -> std::* - // libstdc++ inline namespaces. - // std::__cxx11::* -> std::* - // std::filesystem::__cxx11::* -> std::filesystem::* - if (demangled_name_view.starts_with("std::")) { - char* begin = demangled_name_ptr.get(); - char* to = begin + 5; // std:: - for (char *from = to, *end = begin + demangled_name_view.size(); - from < end;) { - // This is safe, because demangled_name is NUL-terminated. - if (from[0] == '_' && from[1] == '_') { - char* next = from + 1; - while (next < end && *next != ':') next++; - if (next[0] == ':' && next[1] == ':') { - from = next + 2; - continue; - } - } - *to++ = *from++; - } - demangled_name_view = {begin, detail::to_unsigned(to - begin)}; - } - } else { - demangled_name_view = string_view(ti.name()); +#if FMT_USE_RTTI + if (with_typename_) { + out = detail::write_demangled_name(out, typeid(ex)); + *out++ = ':'; + *out++ = ' '; } - out = detail::write_bytes(out, demangled_name_view, spec); -# elif FMT_MSC_VERSION - string_view demangled_name_view(ti.name()); - if (demangled_name_view.starts_with("class ")) - demangled_name_view.remove_prefix(6); - else if (demangled_name_view.starts_with("struct ")) - demangled_name_view.remove_prefix(7); - out = detail::write_bytes(out, demangled_name_view, spec); -# else - out = detail::write_bytes(out, string_view(ti.name()), spec); -# endif - *out++ = ':'; - *out++ = ' '; - return detail::write_bytes(out, string_view(ex.what()), spec); #endif + return detail::write_bytes(out, string_view(ex.what())); } }; @@ -497,7 +610,6 @@ struct is_bit_reference_like> { // We can't use std::vector::reference and // std::bitset::reference because the compiler can't deduce Allocator and N // in partial specialization. -FMT_EXPORT template struct formatter::value>> @@ -509,7 +621,14 @@ struct formatter +auto ptr(const std::unique_ptr& p) -> const void* { + return p.get(); +} +template auto ptr(const std::shared_ptr& p) -> const void* { + return p.get(); +} + template struct formatter, Char, enable_if_t::value>> @@ -522,7 +641,6 @@ struct formatter, Char, }; #ifdef __cpp_lib_atomic_flag_test -FMT_EXPORT template struct formatter : formatter { template @@ -533,5 +651,78 @@ struct formatter : formatter { }; #endif // __cpp_lib_atomic_flag_test +template struct formatter, Char> { + private: + detail::dynamic_format_specs specs_; + + template + FMT_CONSTEXPR auto do_format(const std::complex& c, + detail::dynamic_format_specs& specs, + FormatContext& ctx, OutputIt out) const + -> OutputIt { + if (c.real() != 0) { + *out++ = Char('('); + out = detail::write(out, c.real(), specs, ctx.locale()); + specs.set_sign(sign::plus); + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + *out++ = Char(')'); + return out; + } + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + return out; + } + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type_constant::value); + } + + template + auto format(const std::complex& c, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs = specs_; + if (specs.dynamic()) { + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); + auto buf = basic_memory_buffer(); + + auto outer_specs = format_specs(); + outer_specs.width = specs.width; + outer_specs.copy_fill_from(specs); + outer_specs.set_align(specs.align()); + + specs.width = 0; + specs.set_fill({}); + specs.set_align(align::none); + + do_format(c, specs, ctx, basic_appender(buf)); + return detail::write(ctx.out(), + basic_string_view(buf.data(), buf.size()), + outer_specs); + } +}; + +template +struct formatter, Char, + enable_if_t, Char>::value>> + : formatter, Char> { + template + auto format(std::reference_wrapper ref, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format(ref.get(), ctx); + } +}; + FMT_END_NAMESPACE #endif // FMT_STD_H_ diff --git a/deps/spdlog/include/spdlog/fmt/bundled/xchar.h b/deps/spdlog/include/spdlog/fmt/bundled/xchar.h index f609c5c41a2..90994fff6e2 100644 --- a/deps/spdlog/include/spdlog/fmt/bundled/xchar.h +++ b/deps/spdlog/include/spdlog/fmt/bundled/xchar.h @@ -8,12 +8,16 @@ #ifndef FMT_XCHAR_H_ #define FMT_XCHAR_H_ -#include - +#include "color.h" #include "format.h" - -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -# include +#include "ostream.h" +#include "ranges.h" + +#ifndef FMT_MODULE +# include +# if FMT_USE_LOCALE +# include +# endif #endif FMT_BEGIN_NAMESPACE @@ -22,10 +26,26 @@ namespace detail { template using is_exotic_char = bool_constant::value>; -inline auto write_loc(std::back_insert_iterator> out, - loc_value value, const format_specs& specs, - locale_ref loc) -> bool { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template struct format_string_char {}; + +template +struct format_string_char< + S, void_t())))>> { + using type = char_t; +}; + +template +struct format_string_char< + S, enable_if_t::value>> { + using type = typename S::char_type; +}; + +template +using format_string_char_t = typename format_string_char::type; + +inline auto write_loc(basic_appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool { +#if FMT_USE_LOCALE auto& numpunct = std::use_facet>(loc.get()); auto separator = std::wstring(); @@ -40,42 +60,75 @@ inline auto write_loc(std::back_insert_iterator> out, FMT_BEGIN_EXPORT using wstring_view = basic_string_view; -using wformat_parse_context = basic_format_parse_context; -using wformat_context = buffer_context; +using wformat_parse_context = parse_context; +using wformat_context = buffered_context; using wformat_args = basic_format_args; using wmemory_buffer = basic_memory_buffer; -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -// Workaround broken conversion on older gcc. -template using wformat_string = wstring_view; -inline auto runtime(wstring_view s) -> wstring_view { return s; } -#else -template -using wformat_string = basic_format_string...>; +template struct basic_fstring { + private: + basic_string_view str_; + + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + Char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + using t = basic_fstring; + + template >::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(s, checker(s, arg_pack())); + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { + FMT_CONSTEXPR auto sv = basic_string_view(S()); + FMT_CONSTEXPR int ignore = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(ignore); + } + basic_fstring(runtime_format_string fmt) : str_(fmt.str) {} + + operator basic_string_view() const { return str_; } + auto get() const -> basic_string_view { return str_; } +}; + +template +using basic_format_string = basic_fstring; + +template +using wformat_string = typename basic_format_string::t; inline auto runtime(wstring_view s) -> runtime_format_string { return {{s}}; } -#endif -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 {}; +#ifdef __cpp_char8_t +template <> struct is_char : bool_constant {}; +#endif template -constexpr auto make_wformat_args(const T&... args) - -> format_arg_store { - return {args...}; +constexpr auto make_wformat_args(T&... args) + -> decltype(fmt::make_format_args(args...)) { + return fmt::make_format_args(args...); } +#if !FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { -#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS -constexpr auto operator""_a(const wchar_t* s, size_t) - -> detail::udl_arg { +inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg { return {s}; } -#endif } // namespace literals +#endif template auto join(It begin, Sentinel end, wstring_view sep) @@ -83,9 +136,9 @@ auto join(It begin, Sentinel end, wstring_view sep) return {begin, end, sep}; } -template +template ::value)> auto join(Range&& range, wstring_view sep) - -> join_view, detail::sentinel_t, + -> join_view { return join(std::begin(range), std::end(range), sep); } @@ -96,13 +149,19 @@ auto join(std::initializer_list list, wstring_view sep) return join(std::begin(list), std::end(list), sep); } +template ::value)> +auto join(const Tuple& tuple, basic_string_view sep) + -> tuple_join_view { + return {tuple, sep}; +} + template ::value)> -auto vformat(basic_string_view format_str, - basic_format_args>> args) +auto vformat(basic_string_view fmt, + typename detail::vformat_args::type args) -> std::basic_string { auto buf = basic_memory_buffer(); - detail::vformat_to(buf, format_str, args); - return to_string(buf); + detail::vformat_to(buf, fmt, args); + return {buf.data(), buf.size()}; } template @@ -110,110 +169,122 @@ auto format(wformat_string fmt, T&&... args) -> std::wstring { return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); } +template +auto format_to(OutputIt out, wformat_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt::wstring_view(fmt), + fmt::make_wformat_args(args...)); +} + // Pass char_t as a default template parameter instead of using // std::basic_string> to reduce the symbol size. -template , +template , FMT_ENABLE_IF(!std::is_same::value && !std::is_same::value)> -auto format(const S& format_str, T&&... args) -> std::basic_string { - return vformat(detail::to_string_view(format_str), - fmt::make_format_args>(args...)); +auto format(const S& fmt, T&&... args) -> std::basic_string { + return vformat(detail::to_string_view(fmt), + fmt::make_format_args>(args...)); } -template , +template , FMT_ENABLE_IF(detail::is_locale::value&& detail::is_exotic_char::value)> -inline auto vformat( - const Locale& loc, const S& format_str, - basic_format_args>> args) +inline auto vformat(const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) -> std::basic_string { - return detail::vformat(loc, detail::to_string_view(format_str), args); + auto buf = basic_memory_buffer(); + detail::vformat_to(buf, detail::to_string_view(fmt), args, + detail::locale_ref(loc)); + return {buf.data(), buf.size()}; } -template , +template , FMT_ENABLE_IF(detail::is_locale::value&& detail::is_exotic_char::value)> -inline auto format(const Locale& loc, const S& format_str, T&&... args) +inline auto format(const Locale& loc, const S& fmt, T&&... args) -> std::basic_string { - return detail::vformat(loc, detail::to_string_view(format_str), - fmt::make_format_args>(args...)); + return vformat(loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); } -template , +template , FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_exotic_char::value)> -auto vformat_to(OutputIt out, const S& format_str, - basic_format_args>> args) - -> OutputIt { +auto vformat_to(OutputIt out, const S& fmt, + typename detail::vformat_args::type args) -> OutputIt { auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, detail::to_string_view(format_str), args); + detail::vformat_to(buf, detail::to_string_view(fmt), args); return detail::get_iterator(buf, out); } template , - FMT_ENABLE_IF(detail::is_output_iterator::value&& - detail::is_exotic_char::value)> + typename Char = detail::format_string_char_t, + FMT_ENABLE_IF(detail::is_output_iterator::value && + !std::is_same::value && + !std::is_same::value)> inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { return vformat_to(out, detail::to_string_view(fmt), - fmt::make_format_args>(args...)); + fmt::make_format_args>(args...)); } template , + typename Char = detail::format_string_char_t, FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_locale::value&& detail::is_exotic_char::value)> -inline auto vformat_to( - OutputIt out, const Locale& loc, const S& format_str, - basic_format_args>> args) -> OutputIt { +inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) + -> OutputIt { auto&& buf = detail::get_buffer(out); - vformat_to(buf, detail::to_string_view(format_str), args, - detail::locale_ref(loc)); + vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); return detail::get_iterator(buf, out); } -template , +template , bool enable = detail::is_output_iterator::value && detail::is_locale::value && detail::is_exotic_char::value> -inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, +inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, T&&... args) -> typename std::enable_if::type { - return vformat_to(out, loc, detail::to_string_view(format_str), - fmt::make_format_args>(args...)); + return vformat_to(out, loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); } template ::value&& detail::is_exotic_char::value)> -inline auto vformat_to_n( - OutputIt out, size_t n, basic_string_view format_str, - basic_format_args>> args) +inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, + typename detail::vformat_args::type args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - detail::vformat_to(buf, format_str, args); + detail::vformat_to(buf, fmt, args); return {buf.out(), buf.count()}; } template , + typename Char = detail::format_string_char_t, FMT_ENABLE_IF(detail::is_output_iterator::value&& detail::is_exotic_char::value)> inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) -> format_to_n_result { - return vformat_to_n(out, n, detail::to_string_view(fmt), - fmt::make_format_args>(args...)); + return vformat_to_n(out, n, fmt::basic_string_view(fmt), + fmt::make_format_args>(args...)); } -template , +template , FMT_ENABLE_IF(detail::is_exotic_char::value)> inline auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer(); detail::vformat_to(buf, detail::to_string_view(fmt), - fmt::make_format_args>(args...)); + fmt::make_format_args>(args...)); return buf.count(); } @@ -247,9 +318,48 @@ template void println(wformat_string fmt, T&&... args) { return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); } -/** - Converts *value* to ``std::wstring`` using the default format for type *T*. - */ +inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) + -> std::wstring { + auto buf = wmemory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + return {buf.data(), buf.size()}; +} + +template +inline auto format(text_style ts, wformat_string fmt, T&&... args) + -> std::wstring { + return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string fmt, + const T&... args) { + vprint(f, ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(text_style ts, wformat_string fmt, + const T&... args) { + return print(stdout, ts, fmt, args...); +} + +inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { + auto buffer = basic_memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::write_buffer(os, buffer); +} + +template +void print(std::wostream& os, wformat_string fmt, T&&... args) { + vprint(os, fmt, fmt::make_format_args>(args...)); +} + +template +void println(std::wostream& os, wformat_string fmt, T&&... args) { + print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +/// Converts `value` to `std::wstring` using the default format for type `T`. template inline auto to_wstring(const T& value) -> std::wstring { return format(FMT_STRING(L"{}"), value); } diff --git a/deps/spdlog/include/spdlog/fmt/fmt.h b/deps/spdlog/include/spdlog/fmt/fmt.h index 7fa6b093360..81159e3c174 100644 --- a/deps/spdlog/include/spdlog/fmt/fmt.h +++ b/deps/spdlog/include/spdlog/fmt/fmt.h @@ -21,10 +21,10 @@ #define FMT_USE_WINDOWS_H 0 #endif - #include + #include #include #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib - #include + #include #include #endif diff --git a/deps/spdlog/include/spdlog/mdc.h b/deps/spdlog/include/spdlog/mdc.h index 41f0c1f3fc5..bc131746e86 100644 --- a/deps/spdlog/include/spdlog/mdc.h +++ b/deps/spdlog/include/spdlog/mdc.h @@ -3,17 +3,23 @@ #pragma once +#if defined(SPDLOG_NO_TLS) + #error "This header requires thread local storage support, but SPDLOG_NO_TLS is defined." +#endif + #include #include #include -// MDC is a simple map of key->string values stored in thread local storage whose content will be printed by the loggers. -// Note: Not supported in async mode (thread local storage - so the async thread pool have different copy). +// MDC is a simple map of key->string values stored in thread local storage whose content will be +// printed by the loggers. Note: Not supported in async mode (thread local storage - so the async +// thread pool have different copy). // // Usage example: // spdlog::mdc::put("mdc_key_1", "mdc_value_1"); -// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] [mdc_key_1:mdc_value_1] Hello, World! +// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] +// [mdc_key_1:mdc_value_1] Hello, World! namespace spdlog { class SPDLOG_API mdc { diff --git a/deps/spdlog/include/spdlog/pattern_formatter-inl.h b/deps/spdlog/include/spdlog/pattern_formatter-inl.h index 756e594155b..fd408ed527a 100644 --- a/deps/spdlog/include/spdlog/pattern_formatter-inl.h +++ b/deps/spdlog/include/spdlog/pattern_formatter-inl.h @@ -10,7 +10,11 @@ #include #include #include -#include + +#ifndef SPDLOG_NO_TLS + #include +#endif + #include #include @@ -66,6 +70,9 @@ class scoped_padder { pad_it(remaining_pad_); } else if (padinfo_.truncate_) { long new_size = static_cast(dest_.size()) + remaining_pad_; + if (new_size < 0) { + new_size = 0; + } dest_.resize(static_cast(new_size)); } } @@ -176,7 +183,7 @@ class A_formatter : public flag_formatter { // Abbreviated month static const std::array months{ - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}}; + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}}; template class b_formatter final : public flag_formatter { @@ -260,7 +267,7 @@ class D_formatter final : public flag_formatter { : 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; + const size_t field_size = 8; ScopedPadder p(field_size, padinfo_, dest); fmt_helper::pad2(tm_time.tm_mon + 1, dest); @@ -786,6 +793,7 @@ class elapsed_formatter final : public flag_formatter { // Class for formatting Mapped Diagnostic Context (MDC) in log messages. // Example: [logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message +#ifndef SPDLOG_NO_TLS template class mdc_formatter : public flag_formatter { public: @@ -824,6 +832,7 @@ class mdc_formatter : public flag_formatter { } } }; +#endif // Full info formatter // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v @@ -901,6 +910,7 @@ class full_formatter final : public flag_formatter { dest.push_back(' '); } +#ifndef SPDLOG_NO_TLS // add mdc if present auto &mdc_map = mdc::get_context(); if (!mdc_map.empty()) { @@ -909,6 +919,7 @@ class full_formatter final : public flag_formatter { dest.push_back(']'); dest.push_back(' '); } +#endif // fmt_helper::append_string_view(msg.msg(), dest); fmt_helper::append_string_view(msg.payload, dest); } @@ -916,7 +927,10 @@ class full_formatter final : public flag_formatter { private: std::chrono::seconds cache_timestamp_{0}; memory_buf_t cached_datetime_; - mdc_formatter mdc_formatter_{padding_info{}}; + +#ifndef SPDLOG_NO_TLS + mdc_formatter mdc_formatter_{padding_info {}}; +#endif }; } // namespace details @@ -1211,9 +1225,11 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i padding)); break; +#ifndef SPDLOG_NO_TLS // mdc formatter requires TLS support case ('&'): formatters_.push_back(details::make_unique>(padding)); break; +#endif default: // Unknown flag appears as is auto unknown_flag = details::make_unique(); diff --git a/deps/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h b/deps/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h index 2194f67b94e..6a23f6c70f5 100644 --- a/deps/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/deps/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h @@ -20,7 +20,7 @@ SPDLOG_INLINE ansicolor_sink::ansicolor_sink(FILE *target_file, co formatter_(details::make_unique()) { - set_color_mode(mode); + set_color_mode_(mode); colors_.at(level::trace) = to_string_(white); colors_.at(level::debug) = to_string_(cyan); colors_.at(level::info) = to_string_(green); @@ -82,12 +82,18 @@ SPDLOG_INLINE void ansicolor_sink::set_formatter( } template -SPDLOG_INLINE bool ansicolor_sink::should_color() { +SPDLOG_INLINE bool ansicolor_sink::should_color() const { return should_do_colors_; } template SPDLOG_INLINE void ansicolor_sink::set_color_mode(color_mode mode) { + std::lock_guard lock(mutex_); + set_color_mode_(mode); +} + +template +SPDLOG_INLINE void ansicolor_sink::set_color_mode_(color_mode mode) { switch (mode) { case color_mode::always: should_do_colors_ = true; @@ -105,15 +111,16 @@ SPDLOG_INLINE void ansicolor_sink::set_color_mode(color_mode mode) } template -SPDLOG_INLINE void ansicolor_sink::print_ccode_(const string_view_t &color_code) { - fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); +SPDLOG_INLINE void ansicolor_sink::print_ccode_( + const string_view_t &color_code) const { + details::os::fwrite_bytes(color_code.data(), color_code.size(), target_file_); } template SPDLOG_INLINE void ansicolor_sink::print_range_(const memory_buf_t &formatted, size_t start, - size_t end) { - fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); + size_t end) const { + details::os::fwrite_bytes(formatted.data() + start, end - start, target_file_); } template diff --git a/deps/spdlog/include/spdlog/sinks/ansicolor_sink.h b/deps/spdlog/include/spdlog/sinks/ansicolor_sink.h index ff53730c0d9..19aa421e6c6 100644 --- a/deps/spdlog/include/spdlog/sinks/ansicolor_sink.h +++ b/deps/spdlog/include/spdlog/sinks/ansicolor_sink.h @@ -36,11 +36,11 @@ class ansicolor_sink : public sink { void set_color(level::level_enum color_level, string_view_t color); void set_color_mode(color_mode mode); - bool should_color(); + bool should_color() const; void log(const details::log_msg &msg) override; void flush() override; - void set_pattern(const std::string &pattern) final; + void set_pattern(const std::string &pattern) final override; void set_formatter(std::unique_ptr sink_formatter) override; // Formatting codes @@ -84,8 +84,9 @@ class ansicolor_sink : public sink { bool should_do_colors_; std::unique_ptr formatter_; std::array colors_; - void print_ccode_(const string_view_t &color_code); - void print_range_(const memory_buf_t &formatted, size_t start, size_t end); + void set_color_mode_(color_mode mode); + void print_ccode_(const string_view_t &color_code) const; + void print_range_(const memory_buf_t &formatted, size_t start, size_t end) const; static std::string to_string_(const string_view_t &sv); }; diff --git a/deps/spdlog/include/spdlog/sinks/base_sink.h b/deps/spdlog/include/spdlog/sinks/base_sink.h index 1ca772a6520..1b4bb0689b8 100644 --- a/deps/spdlog/include/spdlog/sinks/base_sink.h +++ b/deps/spdlog/include/spdlog/sinks/base_sink.h @@ -28,10 +28,10 @@ class SPDLOG_API base_sink : public sink { base_sink &operator=(const base_sink &) = delete; base_sink &operator=(base_sink &&) = delete; - void log(const details::log_msg &msg) final; - void flush() final; - void set_pattern(const std::string &pattern) final; - void set_formatter(std::unique_ptr sink_formatter) final; + void log(const details::log_msg &msg) final override; + void flush() final override; + void set_pattern(const std::string &pattern) final override; + void set_formatter(std::unique_ptr sink_formatter) final override; protected: // sink formatter diff --git a/deps/spdlog/include/spdlog/sinks/basic_file_sink-inl.h b/deps/spdlog/include/spdlog/sinks/basic_file_sink-inl.h index f7c1abf7c94..ce0ddad0068 100644 --- a/deps/spdlog/include/spdlog/sinks/basic_file_sink-inl.h +++ b/deps/spdlog/include/spdlog/sinks/basic_file_sink-inl.h @@ -26,6 +26,12 @@ SPDLOG_INLINE const filename_t &basic_file_sink::filename() const { return file_helper_.filename(); } +template +SPDLOG_INLINE void basic_file_sink::truncate() { + std::lock_guard lock(base_sink::mutex_); + file_helper_.reopen(true); +} + template SPDLOG_INLINE void basic_file_sink::sink_it_(const details::log_msg &msg) { memory_buf_t formatted; diff --git a/deps/spdlog/include/spdlog/sinks/basic_file_sink.h b/deps/spdlog/include/spdlog/sinks/basic_file_sink.h index 699caa147de..48c07671aa4 100644 --- a/deps/spdlog/include/spdlog/sinks/basic_file_sink.h +++ b/deps/spdlog/include/spdlog/sinks/basic_file_sink.h @@ -23,6 +23,7 @@ class basic_file_sink final : public base_sink { bool truncate = false, const file_event_handlers &event_handlers = {}); const filename_t &filename() const; + void truncate(); protected: void sink_it_(const details::log_msg &msg) override; diff --git a/deps/spdlog/include/spdlog/sinks/callback_sink.h b/deps/spdlog/include/spdlog/sinks/callback_sink.h index 71668ef28e5..8f0c8d41189 100644 --- a/deps/spdlog/include/spdlog/sinks/callback_sink.h +++ b/deps/spdlog/include/spdlog/sinks/callback_sink.h @@ -27,7 +27,7 @@ class callback_sink final : public base_sink { protected: void sink_it_(const details::log_msg &msg) override { callback_(msg); } - void flush_() override{}; + void flush_() override {} private: custom_log_callback callback_; diff --git a/deps/spdlog/include/spdlog/sinks/daily_file_sink.h b/deps/spdlog/include/spdlog/sinks/daily_file_sink.h index 1b1dc44a872..615c9f7be3d 100644 --- a/deps/spdlog/include/spdlog/sinks/daily_file_sink.h +++ b/deps/spdlog/include/spdlog/sinks/daily_file_sink.h @@ -62,6 +62,8 @@ struct daily_filename_format_calculator { * Rotating file sink based on date. * If truncate != false , the created file will be truncated. * If max_files > 0, retain only the last max_files and delete previous. + * Note that old log files from previous executions will not be deleted by this class, + * rotation and deletion is only applied while the program is running. */ template class daily_file_sink final : public base_sink { diff --git a/deps/spdlog/include/spdlog/sinks/dup_filter_sink.h b/deps/spdlog/include/spdlog/sinks/dup_filter_sink.h index 1498142c47e..0588d772172 100644 --- a/deps/spdlog/include/spdlog/sinks/dup_filter_sink.h +++ b/deps/spdlog/include/spdlog/sinks/dup_filter_sink.h @@ -40,22 +40,21 @@ template class dup_filter_sink : public dist_sink { public: template - explicit dup_filter_sink(std::chrono::duration max_skip_duration, - level::level_enum notification_level = level::info) - : max_skip_duration_{max_skip_duration}, - log_level_{notification_level} {} + explicit dup_filter_sink(std::chrono::duration max_skip_duration) + : max_skip_duration_{max_skip_duration} {} protected: std::chrono::microseconds max_skip_duration_; log_clock::time_point last_msg_time_; std::string last_msg_payload_; size_t skip_counter_ = 0; - level::level_enum log_level_; + level::level_enum skipped_msg_log_level_ = spdlog::level::level_enum::off; void sink_it_(const details::log_msg &msg) override { bool filtered = filter_(msg); if (!filtered) { skip_counter_ += 1; + skipped_msg_log_level_ = msg.level; return; } @@ -65,7 +64,7 @@ class dup_filter_sink : public dist_sink { auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast(skip_counter_)); if (msg_size > 0 && static_cast(msg_size) < sizeof(buf)) { - details::log_msg skipped_msg{msg.source, msg.logger_name, log_level_, + details::log_msg skipped_msg{msg.source, msg.logger_name, skipped_msg_log_level_, string_view_t{buf, static_cast(msg_size)}}; dist_sink::sink_it_(skipped_msg); } diff --git a/deps/spdlog/include/spdlog/sinks/hourly_file_sink.h b/deps/spdlog/include/spdlog/sinks/hourly_file_sink.h index 1f138925fca..3e618725bae 100644 --- a/deps/spdlog/include/spdlog/sinks/hourly_file_sink.h +++ b/deps/spdlog/include/spdlog/sinks/hourly_file_sink.h @@ -39,6 +39,8 @@ struct hourly_filename_calculator { * Rotating file sink based on time. * If truncate != false , the created file will be truncated. * If max_files > 0, retain only the last max_files and delete previous. + * Note that old log files from previous executions will not be deleted by this class, + * rotation and deletion is only applied while the program is running. */ template class hourly_file_sink final : public base_sink { diff --git a/deps/spdlog/include/spdlog/sinks/msvc_sink.h b/deps/spdlog/include/spdlog/sinks/msvc_sink.h index 2e5f6875eaf..c28d6ebd738 100644 --- a/deps/spdlog/include/spdlog/sinks/msvc_sink.h +++ b/deps/spdlog/include/spdlog/sinks/msvc_sink.h @@ -32,7 +32,7 @@ class msvc_sink : public base_sink { public: msvc_sink() = default; msvc_sink(bool check_debugger_present) - : check_debugger_present_{check_debugger_present} {}; + : check_debugger_present_{check_debugger_present} {} protected: void sink_it_(const details::log_msg &msg) override { diff --git a/deps/spdlog/include/spdlog/sinks/null_sink.h b/deps/spdlog/include/spdlog/sinks/null_sink.h index c51247fe66b..74530b5b1a3 100644 --- a/deps/spdlog/include/spdlog/sinks/null_sink.h +++ b/deps/spdlog/include/spdlog/sinks/null_sink.h @@ -13,7 +13,7 @@ namespace spdlog { namespace sinks { template -class null_sink : public base_sink { +class null_sink final : public base_sink { protected: void sink_it_(const details::log_msg &) override {} void flush_() override {} diff --git a/deps/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h b/deps/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h index 6f10256fc65..a3694d86675 100644 --- a/deps/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/deps/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -38,8 +37,8 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero"); } - if (max_files > 200000) { - throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000"); + if (max_files > MaxFiles) { + throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed MaxFiles"); } file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once @@ -54,13 +53,14 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( template SPDLOG_INLINE filename_t rotating_file_sink::calc_filename(const filename_t &filename, std::size_t index) { - if (index == 0u) { + if (index == 0U) { return filename; } - filename_t basename, ext; + filename_t basename; + filename_t ext; std::tie(basename, ext) = details::file_helper::split_by_extension(filename); - return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); + return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}.{}{}")), basename, index, ext); } template @@ -69,6 +69,41 @@ SPDLOG_INLINE filename_t rotating_file_sink::filename() { return file_helper_.filename(); } +template +SPDLOG_INLINE void rotating_file_sink::rotate_now() { + std::lock_guard lock(base_sink::mutex_); + rotate_(); +} +template +SPDLOG_INLINE void rotating_file_sink::set_max_size(std::size_t max_size) { + std::lock_guard lock(base_sink::mutex_); + if (max_size == 0) { + throw_spdlog_ex("rotating sink set_max_size: max_size arg cannot be zero"); + } + max_size_ = max_size; +} + +template +SPDLOG_INLINE std::size_t rotating_file_sink::get_max_size() { + std::lock_guard lock(base_sink::mutex_); + return max_size_; +} + +template +SPDLOG_INLINE void rotating_file_sink::set_max_files(std::size_t max_files) { + std::lock_guard lock(base_sink::mutex_); + if (max_files > MaxFiles) { + throw_spdlog_ex("rotating sink set_max_files: max_files arg cannot exceed 200000"); + } + max_files_ = max_files; +} + +template +std::size_t rotating_file_sink::get_max_files() { + std::lock_guard lock(base_sink::mutex_); + return max_files_; +} + template SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &msg) { memory_buf_t formatted; diff --git a/deps/spdlog/include/spdlog/sinks/rotating_file_sink.h b/deps/spdlog/include/spdlog/sinks/rotating_file_sink.h index cd43d349d08..72302e69dc6 100644 --- a/deps/spdlog/include/spdlog/sinks/rotating_file_sink.h +++ b/deps/spdlog/include/spdlog/sinks/rotating_file_sink.h @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -21,6 +20,7 @@ namespace sinks { template class rotating_file_sink final : public base_sink { public: + static constexpr size_t MaxFiles = 200000; rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, @@ -28,6 +28,11 @@ class rotating_file_sink final : public base_sink { const file_event_handlers &event_handlers = {}); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); + void rotate_now(); + void set_max_size(std::size_t max_size); + std::size_t get_max_size(); + void set_max_files(std::size_t max_files); + std::size_t get_max_files(); protected: void sink_it_(const details::log_msg &msg) override; @@ -41,7 +46,7 @@ class rotating_file_sink final : public base_sink { // log.3.txt -> delete void rotate_(); - // delete the target if exists, and rename the src file to target + // delete the target if exists, and rename the src file to target // return true on success, false otherwise. bool rename_file_(const filename_t &src_filename, const filename_t &target_filename); @@ -60,25 +65,24 @@ using rotating_file_sink_st = rotating_file_sink; // // factory functions // - template -inline std::shared_ptr rotating_logger_mt(const std::string &logger_name, - const filename_t &filename, - size_t max_file_size, - size_t max_files, - bool rotate_on_open = false, - const file_event_handlers &event_handlers = {}) { +std::shared_ptr rotating_logger_mt(const std::string &logger_name, + const filename_t &filename, + size_t max_file_size, + size_t max_files, + bool rotate_on_open = false, + const file_event_handlers &event_handlers = {}) { return Factory::template create( logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } template -inline std::shared_ptr rotating_logger_st(const std::string &logger_name, - const filename_t &filename, - size_t max_file_size, - size_t max_files, - bool rotate_on_open = false, - const file_event_handlers &event_handlers = {}) { +std::shared_ptr rotating_logger_st(const std::string &logger_name, + const filename_t &filename, + size_t max_file_size, + size_t max_files, + bool rotate_on_open = false, + const file_event_handlers &event_handlers = {}) { return Factory::template create( logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } diff --git a/deps/spdlog/include/spdlog/sinks/stdout_sinks-inl.h b/deps/spdlog/include/spdlog/sinks/stdout_sinks-inl.h index f98244db292..dcb21d84a73 100644 --- a/deps/spdlog/include/spdlog/sinks/stdout_sinks-inl.h +++ b/deps/spdlog/include/spdlog/sinks/stdout_sinks-inl.h @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef _WIN32 // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) @@ -22,7 +23,7 @@ #include // _get_osfhandle(..) #include // _fileno(..) -#endif // WIN32 +#endif // _WIN32 namespace spdlog { @@ -44,7 +45,7 @@ SPDLOG_INLINE stdout_sink_base::stdout_sink_base(FILE *file) if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr) { throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno); } -#endif // WIN32 +#endif // _WIN32 } template @@ -67,8 +68,8 @@ SPDLOG_INLINE void stdout_sink_base::log(const details::log_msg &m std::lock_guard lock(mutex_); memory_buf_t formatted; formatter_->format(msg, formatted); - ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_); -#endif // WIN32 + details::os::fwrite_bytes(formatted.data(), formatted.size(), file_); +#endif // _WIN32 ::fflush(file_); // flush every line to terminal } diff --git a/deps/spdlog/include/spdlog/sinks/syslog_sink.h b/deps/spdlog/include/spdlog/sinks/syslog_sink.h index a2df3b44ded..913d41be61c 100644 --- a/deps/spdlog/include/spdlog/sinks/syslog_sink.h +++ b/deps/spdlog/include/spdlog/sinks/syslog_sink.h @@ -64,13 +64,14 @@ class syslog_sink : public base_sink { // // Simply maps spdlog's log level to syslog priority level. // - int syslog_prio_from_level(const details::log_msg &msg) const { + virtual int syslog_prio_from_level(const details::log_msg &msg) const { return syslog_levels_.at(static_cast(msg.level)); } -private: using levels_array = std::array; levels_array syslog_levels_; + +private: // must store the ident because the man says openlog might use the pointer as // is and not a string copy const std::string ident_; diff --git a/deps/spdlog/include/spdlog/sinks/wincolor_sink-inl.h b/deps/spdlog/include/spdlog/sinks/wincolor_sink-inl.h index 696db566370..a9c0fa25c76 100644 --- a/deps/spdlog/include/spdlog/sinks/wincolor_sink-inl.h +++ b/deps/spdlog/include/spdlog/sinks/wincolor_sink-inl.h @@ -137,10 +137,10 @@ void SPDLOG_INLINE wincolor_sink::print_range_(const memory_buf_t #if defined(SPDLOG_UTF8_TO_WCHAR_CONSOLE) wmemory_buf_t wformatted; details::os::utf8_to_wstrbuf(string_view_t(formatted.data() + start, end - start), - wformatted); + wformatted); auto size = static_cast(wformatted.size()); auto ignored = ::WriteConsoleW(static_cast(out_handle_), wformatted.data(), size, - nullptr, nullptr); + nullptr, nullptr); #else auto size = static_cast(end - start); auto ignored = ::WriteConsoleA(static_cast(out_handle_), formatted.data() + start, diff --git a/deps/spdlog/include/spdlog/spdlog-inl.h b/deps/spdlog/include/spdlog/spdlog-inl.h index 97c362222ed..e02081fe393 100644 --- a/deps/spdlog/include/spdlog/spdlog-inl.h +++ b/deps/spdlog/include/spdlog/spdlog-inl.h @@ -59,6 +59,10 @@ SPDLOG_INLINE void register_logger(std::shared_ptr logger) { details::registry::instance().register_logger(std::move(logger)); } +SPDLOG_INLINE void register_or_replace(std::shared_ptr logger) { + details::registry::instance().register_or_replace(std::move(logger)); +} + SPDLOG_INLINE void apply_all(const std::function)> &fun) { details::registry::instance().apply_all(fun); } diff --git a/deps/spdlog/include/spdlog/spdlog.h b/deps/spdlog/include/spdlog/spdlog.h index a8afbcec4a9..1a927ffc924 100644 --- a/deps/spdlog/include/spdlog/spdlog.h +++ b/deps/spdlog/include/spdlog/spdlog.h @@ -25,7 +25,7 @@ namespace spdlog { using default_factory = synchronous_factory; // Create and register a logger with a templated sink type -// The logger's level, formatter and flush level will be set according the +// The logger's level, formatter and flush level will be set according to the // global settings. // // Example: @@ -46,7 +46,7 @@ inline std::shared_ptr create(std::string logger_name, SinkArgs // spdlog::initialize_logger(mylogger); SPDLOG_API void initialize_logger(std::shared_ptr logger); -// Return an existing logger or nullptr if a logger with such name doesn't +// Return an existing logger or nullptr if a logger with such a name doesn't // exist. // example: spdlog::get("my_logger")->info("hello {}", "world"); SPDLOG_API std::shared_ptr get(const std::string &name); @@ -71,13 +71,13 @@ SPDLOG_API void dump_backtrace(); // Get global logging level SPDLOG_API level::level_enum get_level(); -// Set global logging level +// Set the global logging level SPDLOG_API void set_level(level::level_enum log_level); // Determine whether the default logger should log messages with a certain level SPDLOG_API bool should_log(level::level_enum lvl); -// Set global flush level +// Set a global flush level SPDLOG_API void flush_on(level::level_enum log_level); // Start/Restart a periodic flusher thread @@ -91,9 +91,14 @@ inline void flush_every(std::chrono::duration interval) { SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg)); // Register the given logger with the given name +// Will throw if a logger with the same name already exists. SPDLOG_API void register_logger(std::shared_ptr logger); -// Apply a user defined function on all registered loggers +// Register the given logger with the given name +// Will replace any existing logger with the same name. +SPDLOG_API void register_or_replace(std::shared_ptr logger); + +// Apply a user-defined function on all registered loggers // Example: // spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); SPDLOG_API void apply_all(const std::function)> &fun); @@ -111,19 +116,19 @@ SPDLOG_API void shutdown(); SPDLOG_API void set_automatic_registration(bool automatic_registration); // API for using default logger (stdout_color_mt), -// e.g: spdlog::info("Message {}", 1); +// e.g.: spdlog::info("Message {}", 1); // // The default logger object can be accessed using the spdlog::default_logger(): // For example, to add another sink to it: // spdlog::default_logger()->sinks().push_back(some_sink); // -// The default logger can replaced using spdlog::set_default_logger(new_logger). +// The default logger can be replaced using spdlog::set_default_logger(new_logger). // For example, to replace it with a file logger. // // IMPORTANT: // The default API is thread safe (for _mt loggers), but: // set_default_logger() *should not* be used concurrently with the default API. -// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. +// e.g., do not call set_default_logger() from one thread while calling spdlog::info() from another. SPDLOG_API std::shared_ptr default_logger(); diff --git a/deps/spdlog/include/spdlog/tweakme.h b/deps/spdlog/include/spdlog/tweakme.h index a47a90764a5..d60929878a5 100644 --- a/deps/spdlog/include/spdlog/tweakme.h +++ b/deps/spdlog/include/spdlog/tweakme.h @@ -104,6 +104,13 @@ // // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY // CRITICAL", "OFF" } +// +// For C++17 use string_view_literals: +// +// #include +// using namespace std::string_view_literals; +// #define SPDLOG_LEVEL_NAMES { "MY TRACE"sv, "MY DEBUG"sv, "MY INFO"sv, "MY WARNING"sv, "MY +// ERROR"sv, "MY CRITICAL"sv, "OFF"sv } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/deps/spdlog/include/spdlog/version.h b/deps/spdlog/include/spdlog/version.h index 4fa94cb68cc..d89947b59ca 100644 --- a/deps/spdlog/include/spdlog/version.h +++ b/deps/spdlog/include/spdlog/version.h @@ -4,8 +4,8 @@ #pragma once #define SPDLOG_VER_MAJOR 1 -#define SPDLOG_VER_MINOR 14 -#define SPDLOG_VER_PATCH 1 +#define SPDLOG_VER_MINOR 15 +#define SPDLOG_VER_PATCH 3 #define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch) #define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH) diff --git a/deps/spdlog/src/bundled_fmtlib_format.cpp b/deps/spdlog/src/bundled_fmtlib_format.cpp index 9c1aa594bc8..267a8ed1d2a 100644 --- a/deps/spdlog/src/bundled_fmtlib_format.cpp +++ b/deps/spdlog/src/bundled_fmtlib_format.cpp @@ -16,7 +16,8 @@ namespace detail { template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; - #ifndef FMT_STATIC_THOUSANDS_SEPARATOR + #if FMT_USE_LOCALE +// DEPRECATED! locale_ref in the detail namespace template FMT_API locale_ref::locale_ref(const std::locale& loc); template FMT_API auto locale_ref::get() const -> std::locale; #endif @@ -26,8 +27,10 @@ template FMT_API auto locale_ref::get() const -> std::locale; template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> char; +// DEPRECATED! template FMT_API void buffer::append(const char*, const char*); +// DEPRECATED! template FMT_API void vformat_to(buffer&, string_view, typename vformat_args<>::type, diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 3275a631635..fd63e5b6d0c 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -11,7 +11,7 @@ # Documents produced by Doxygen are derivative works derived from the # input used in their production; they are not affected by this license. -include (${TOP}/cmake/version.cmake) +include (${PROJECT_SOURCE_DIR}/cmake/version.cmake) string(TIMESTAMP DATE "%d-%m-%Y") string(TIMESTAMP YEAR "%Y") @@ -115,6 +115,7 @@ set(DOC_FILES install.dox layout_index_and_notreeview.png layout_index_and_treeview.png + layout_index_and_sidebar.png layout_noindex_and_notreeview.png layout_noindex_and_treeview.png layout_noindex_and_sidebar.png @@ -136,11 +137,16 @@ set(DOC_FILES ) set(DOC_FILES_IMAGES - add.png - del.png - file.png - folder.png - refresh.png + add.eps + add.svg + del.eps + del.svg + file.eps + file.svg + folder.eps + folder.svg + refresh.eps + refresh.svg ) if (build_doc_chm) @@ -149,7 +155,7 @@ if (build_doc_chm) ) endif () -file(GLOB LANG_FILES CONFIGURE_DEPENDS "${TOP}/src//translator_??.h") +file(GLOB LANG_FILES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src//translator_??.h") file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/src ${PROJECT_BINARY_DIR}/doc @@ -203,8 +209,8 @@ set_source_files_properties(language.dox PROPERTIES GENERATED 1) # doc/config.dox (see tag Doxyfile:INPUT) add_custom_command( - COMMAND ${Python_EXECUTABLE} ${TOP}/src/configgen.py -doc ${TOP}/src/config.xml > config.dox - DEPENDS ${TOP}/src/config.xml ${TOP}/src/configgen.py + COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/src/configgen.py -doc ${PROJECT_SOURCE_DIR}/src/config.xml > config.dox + DEPENDS ${PROJECT_SOURCE_DIR}/src/config.xml ${PROJECT_SOURCE_DIR}/src/configgen.py OUTPUT config.dox WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/doc/ ) @@ -252,7 +258,7 @@ if (build_doc_chm) add_custom_target(docs_chm COMMENT "Generating CHM documentation." COMMAND ${CMAKE_COMMAND} -E env VERSION=${VERSION} HTML_HELP_COMPILER=${HTML_HELP_COMPILER} INDEX_DOC=index_html.dox ${DOXYGEN_EXECUTABLE} Doxyfile_chm - COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_BINARY_DIR}/html/examples ${PROJECT_BINARY_DIR}/chm/examples + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_BINARY_DIR}/chm/html/examples ${PROJECT_BINARY_DIR}/chm/examples COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/chm/index.hhp ${PROJECT_BINARY_DIR}/chm/doxygen_manual.hhp COMMAND ${CMAKE_COMMAND} -E echo "import os" > ${PROJECT_BINARY_DIR}/chm/doxygen_manual_examples_chm.py COMMAND ${CMAKE_COMMAND} -E echo "for root, dirs, files in os.walk('examples'):" >> ${PROJECT_BINARY_DIR}/chm/doxygen_manual_examples_chm.py @@ -265,7 +271,7 @@ add_custom_target(docs_chm DEPENDS ${PROJECT_BINARY_DIR}/doc/language.dox ${PROJECT_BINARY_DIR}/doc/config.dox DEPENDS ${OUT_DOC_FILES} DEPENDS ${OUT_DOC_CHM_FILES} - DEPENDS examples + DEPENDS examples_chm DEPENDS doxygen WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/doc/ ) diff --git a/doc/Doxyfile b/doc/Doxyfile index 0ba5884a9da..672f45a94c6 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -22,6 +22,7 @@ QUIET = YES WARNINGS = YES DISABLE_INDEX = YES GENERATE_TREEVIEW = YES +PAGE_OUTLINE_PANEL = NO EXTRACT_ALL = NO EXTRACT_PRIVATE = NO GENERATE_MAN = NO @@ -30,7 +31,7 @@ GENERATE_HTML = $(GENERATE_HTML) GENERATE_HTMLHELP = NO GENERATE_RTF = NO GENERATE_XML = NO -HTML_COLORSTYLE_SAT = 0 +HTML_COLORSTYLE_SAT = 40 ENABLED_SECTIONS = logo_on ENABLE_PREPROCESSING = NO HTML_COLORSTYLE = TOGGLE @@ -58,11 +59,15 @@ HTML_EXTRA_FILES += translator_report.txt ALIASES = LaTeX="\f({\LaTeX}\f)" ALIASES += TeX="\f({\TeX}\f)" ALIASES += forceNewPage="\latexonly \newpage \endlatexonly" -ALIASES += startalign=" \latexonly\noalign{\endlatexonly" -ALIASES += endalign=" \latexonly}\endlatexonly" -ALIASES += startendhtmltag{1}="\startalign\anchor htmltag_\1 \addindex \"\<\1\>\" ^^ \anchor htmltag_end\1 \addindex \"\\"\endalign \<\1\> / \" +ALIASES += startalign= +ALIASES += "endalign=\ilinebr" +ALIASES += starthtmltag{1}="\anchor htmltag_\1 \addindex \"\<\1\>\"\endalign\<\1\>" +ALIASES += endhtmltag{1}="\anchor htmltag_end\1 \addindex \"\\"\endalign\" +ALIASES += "startendhtmltag{1}=\starthtmltag{\1} / \endhtmltag{\1}" + LATEX_BATCHMODE = YES LATEX_EXTRA_STYLESHEET = manual.sty LATEX_EMOJI_DIRECTORY = ../doc WARN_AS_ERROR = FAIL_ON_WARNINGS HTML_PROJECT_COOKIE = doxygen_docs +MARKDOWN_STRICT = NO diff --git a/doc/arch.dox b/doc/arch.dox index 57bebca6ec3..7066b006dd2 100644 --- a/doc/arch.dox +++ b/doc/arch.dox @@ -89,7 +89,7 @@ The preprocessed input buffer is fed to the language parser, which is implemented as a big state machine using \c flex. It can be found in the file \c src/scanner.l. There is one parser for all languages (C/C++/Java/IDL). The state variables \c insideIDL -and \c insideJava are uses at some places for language specific choices. +and \c insideJava are used at some places for language specific choices. The task of the parser is to convert the input buffer into a tree of entries (basically an abstract syntax tree). An entry is defined in \c src/entry.h @@ -256,7 +256,7 @@ debug output goes by default to `stdout` unless one uses `-d stderr`.

Testing

-Doxygen has a small set of tests available to test, some, code integrity. +Doxygen has a small set of tests available to test code integrity. The tests can be run by means of the command `make tests`. When only one or a few tests are required one can set the variable \c TEST_FLAGS when running the test e.g. `make TEST_FLAGS="--id 5" tests` or for multiple tests diff --git a/doc/changelog.dox b/doc/changelog.dox index 1d491014162..0fc725c45a5 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1,5 +1,779 @@ /** \page changelog Changelog \tableofcontents{html,latex} +\section log_1_15 1.15 Series +\subsection log_1_15_0 Release 1.15.0 +\htmlonly +(release date 22-10-2025) +

+ +

Features

+ + +

Minor incompatibilities

+
    +
  • New option MARKDOWN_STRICT which is enabled by default, and will no longer treat ' as the end of a quoted text started with ` +(e.g. `word'). If you depend on the old behavior, set this option to NO.
  • +
+ +

Bug fixes

+
    +
  • issue #8788 Quotes in inline code confuse Markdown parser [view]
  • +
  • issue #11241 [BUG] FORCE_LOCAL_INCLUDES doesn't work with groups [view]
  • +
  • issue #11492 `@plantumlfile` command not recognized [view]
  • +
  • issue #11515 Some markdown does not render after non-breaking spaces [view]
  • +
  • issue #11583 The search functionality does not find items any more since SHA-1 2604e3f in large projects [view]
  • +
  • issue #11587 Page outline navigation panel Glitch [view], [view], [view]
  • +
  • issue #11590 Invalid LaTeX caused by unclosed markdown codeblocks. No warnings provided [view]
  • +
  • issue #11593 Missing links for LaTex, RTF, ... with IMPLICIT_DIR_DOCS [view], [view]
  • +
  • issue #11600 Images with read-only file attributes can fail to copy [view], [view]
  • +
  • issue #11607 Broken HTML tables [view], [view], [view]
  • +
  • issue #11614 Multiple occurrences of the same macro definition in a file result are assigned the same ID [view]
  • +
  • issue #11619 Wrong Python module separator [view]
  • +
  • issue #11621 Markdown code handling `` ` `` gets confused by unterminated `` ` `` within (introduced by 1.11) [view], [view]
  • +
  • issue #11621 Markdown code handling ``` gets confused by unterminated ` within (introduced by 1.11) [view]
  • +
  • issue #11624 Classes always ordered alphabetically [view]
  • +
  • issue #11625 Autolinking does not work with USE_MDFILE_AS_MAINPAGE [view], [view]
  • +
  • issue #11631 Doxygen generates incorrect links for nested README.md files in subfolders [view]
  • +
  • issue #11634 Doxygen Latex Tables not working correctly [view]
  • +
  • issue #11644 Plain '*'-characters in ALIAS argument get removed [view]
  • +
  • issue #11647 Backslashes not properly escaped in generated search files [view]
  • +
  • issue #11652 Support @qualifier for #define [view]
  • +
  • issue #11656 Comment block begin is handled inside C++ raw string literal with double quotes [view]
  • +
  • issue #11658 Support for auto linking to #defines identifiers within markdown code spans [view]
  • +
  • issue #11660 Conditions in HTML table seem to be broken (1.13.2 -> 1.14.0) [view]
  • +
  • issue #11661 <tt> tags for markdown headings with inline code under a topic [view]
  • +
  • issue #11663 [C++] [BUG] Constructor overload with template [view]
  • +
  • issue #11668 [BUG] Emphasis with asterisks (*) does not work when at the beginning of a line of an ALIASES argument [view]
  • +
  • issue #11674 [BUG] When the (group title) is the same as a file name the link on the groups page points to the verbatim source [view]
  • +
  • issue #11676 Final period (.) is missing in the @brief if it terminates with () or a markdown code spans [view]
  • +
  • issue #11679 Incomplete outputs on Java classes with recursive generic types [view]
  • +
  • issue #11682 Release Version 1.14.0 for Linux Segfaults/Crashes [view]
  • +
  • issue #11683 duplicates in searchdata.xml "text" field [view]
  • +
  • issue #11698 Q_PROPERTY wrongly parsed when split on multiple line after READ attribute [view]
  • +
  • issue #11699 Improve Windows performance launching external processes [view]
  • +
  • issue #11708 [BUG] Variadic macro expansion doesn't work when the arguments contain comments starting with // [view]
  • +
  • issue #11714 Source is not readable warning for excluded files [view]
  • +
  • issue #11716 [BUG] In the treeview the links to the undocumented ENUMERATION CONSTANTS don't work. [view]
  • +
  • issue #11717 [BUG] In the page-nav the undocumented ENUMERATION CONSTANTS become part of the enumerated type's name [view], [view]
  • +
  • issue #11718 [BUG] Variadic macro expansion doesn't work when the arguments contain comments starting with /*!< [view]
  • +
  • issue #11721 Doxygen internal Preprocessor cannot process digit group separator in C++ [view]
  • +
  • issue #11722 Doxygen GUI frontend cannot process value with space [view]
  • +
  • issue #11725 [BUG] Single-line comments on members passed as arguments to a macro are treated as detailed description [view]
  • +
  • issue #11740 Regression: Doxygen >= 1.11.0 no longer able to produce .chm files that link to each other using TAGFILES [view]
  • +
  • issue #11742 Handling \copydoc inside table [view]
  • +
  • issue #11748 ALIASES expansion to form `@section` key causes : warning: Invalid section id ' '; ignoring section [view]
  • +
  • issue #11751 Latex documentation with many functions causes pdflatex to hit memory limit [view]
  • +
  • issue #11755 Behavior of \tableofcontents is different in V.1.14.0 and prior version [view]
  • +
  • issue #11756 Friend constructor is recognized as member function [view]
  • +
  • issue #11758 Const parameter in implementation can confuse function signature matching [view]
  • +
  • issue #11761 Missing documentation for specialization with using declaration [view]
  • +
  • issue #11772 Test 009_bug fails [view]
  • +
  • issue #11782 Groups in XML output do not reference C++ concepts [view]
  • +
  • issue #11786 Source code misaligned when using TAB_SIZE = 8 [view]
  • +
  • issue #11787 Documentation of functions that differ in their trailing return types get merged together [view]
  • +
  • issue #11798 @ref remains in html [view]
  • +
  • issue #11799 Incorrect link for template argument [view]
  • +
  • issue #11812 How to linebreak inside long titles in a HTML description list [view] and [view]
  • +
  • Fix handling of trailing return types with `delete` and `default` [view]
  • +
  • Various table related fixes and improvements [view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], and +[view]
  • +
  • Spurious asterisk after preprocessing when using macros [view]
  • +
  • References with an explicit global scope, e.g ::f could still link to a member f with the same name. [view]
  • +
  • Handling of backticks in bibliography [view]
  • +
  • Handling of TIMESTAMP in html footer and header [view]
  • +
  • Handling storage class specifier `thread_local` [view], [view], [view]
  • +
  • Fixed issue causing invalid HTML due to spurious </p>. [view]
  • +
  • Fixed issue rendering Objective-C protocol classes in the HTML output [view]
  • +
  • Don't load Mathjax in source browser files [view]
  • +
  • Fix array indexing issue in markdown.cpp [view]
  • +
  • Potential memory leak [view]
  • +
  • Avoid input buffer overflow [view]
  • +
  • Prevent text showing up as formula when USE_MATHJAX=YES [view]
  • +
  • Added missing structural command to "isOtherPage" list [view]
  • +
  • Code references with an explicit global scope, e.g ::f could still link to a member f with the same name. [view]
  • +
  • Consistent use of `{CMD}` [view]
  • +
  • Further improve quote handling [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Update docs to point user to the correct version of winflexbison [view]
  • +
  • Fix spelling and grammar [view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], +[view], and +[view]
  • +
  • Spelling correction [view], [view], [view]
  • +
  • Consistency language version table between HTML and LaTeX [view]
  • +
  • Inconsistencies in language maintainers email addresses [view]
  • +
  • Incorrect warning regarding type and start of backtick type of block [view]
  • +
  • Limit warnings about unmatched */ to code blocks [view]
  • +
+ +

Refactoring and cleanup

+
    +
  • qt: Default to Qt 6 instead of Qt 5 [view]
  • +
  • refactor(aliases): simplify findEndOfCommand for clarity [view]
  • +
  • refactor(alphaindextablecell): remove classDef, letter, row and column methods [view]
  • +
  • refactor(anchor): simplify addPrefixIfNeeded [view]
  • +
  • refactor(cite): simplify latexBibFiles and find [view]
  • +
  • refactor(clangparser): replace old-style nested namespaces with C++17 shorthand [view]
  • +
  • refactor(classdef): simplify insertUsedFile [view]
  • +
  • refactor(debuglex): simplify print method [view]
  • +
  • refactor(definition): simplify toDefinition and toDefinitionMutable functions [view]
  • +
  • refactor(dotrunner): simplify checkPngResult function [view]
  • +
  • refactor(doxygen): simplify haveEqualFileNames function [view]
  • +
  • refactor(doxyindexer): replace if-else statements with map lookup [view]
  • +
  • refactor(doxyindexer): update includes [view]
  • +
  • refactor(doxyparse): improve ignoreStaticExternalCall function readability [view]
  • +
  • refactor(doxyparse): remove unnecessary include [view]
  • +
  • refactor(doxyparse): replace system("rm -rf") with std::filesystem::remove_all [view]
  • +
  • refactor(doxyparse): simplify sanitizeString function [view]
  • +
  • refactor(doxysearch): modernize dirExists function [view], [view]
  • +
  • refactor(doxysearch): remove unnecessary includes [view]
  • +
  • refactor(doxysearch): replace field macros with constexpr Xapian::valueno constants [view]
  • +
  • refactor(doxysearch): replace HEX2DEC macro with function [view]
  • +
  • refactor: remove dead code [view], +[view], +[view], +[view], +[view], +[view], and +[view]
  • +
  • refactor(pagedef): simplify overloaded toPageDef functions [view]
  • +
  • refactor(util): add missing braces [view]
  • +
  • refactor(util): simplify writeString and writeBreak methods [view]
  • +
  • Refactoring: add constexpr/noexcept to types.h [view]
  • +
  • Refactoring: code formatting and fix coverity warnings [view]
  • +
  • Refactoring: Let alias command parsing use the new reg::Ex features [view]
  • +
  • Refactoring: pack optional params of generateDoc/validatingParseDoc into a class [view]
  • +
  • Refactoring: reducing unneeded string conversions [view]
  • +
  • Refactoring: remove old getDefs routine (dead code) [view]
  • +
  • [libmd5] Smaller code cleanups [view]
  • +
  • [mscgen] Declared some variables as pointer to const [view]
  • +
  • [mscgen] Fix copy & past bug, probably max quality was meant [view]
  • +
  • [mscgen] Make variables more local [view]
  • +
  • fix(cite): remove unnecessary semicolon [view]
  • +
  • fix(classdef): warning [cppcoreguidelines-pro-type-cstyle-cast] [view]
  • +
  • fix(datetime): warning [performance-unnecessary-copy-initialization] [view]
  • +
  • fix(debug): warning [bugprone-narrowing-conversions] [view]
  • +
  • fix(definition): remove lname variable [view]
  • +
  • fix(definition): warning [performance-unnecessary-copy-initialization] [view], [view]
  • +
  • fix(docnode): warning [clang-analyzer-deadcode.DeadStores] [view]
  • +
  • fix(dotdirdeps): warning [performance-move-const-arg] [view]
  • +
  • fix(dotnode): remove p variable [view]
  • +
  • fix(dotnode): warning [performance-unnecessary-copy-initialization] [view]
  • +
  • fix(doxygen): warning [clang-analyzer-deadcode.DeadStores] [view]
  • +
  • fix(doxygen): warning [performance-unnecessary-copy-initialization] [view]
  • +
  • fix(doxyindexer): handle negative tellg method return value [view]
  • +
  • fix(doxysearch): remove duplicate definition [view]
  • +
  • fix(doxysearch): remove inner callback, keep outer [view]
  • +
  • fix(doxysearch): use int instead of char to handle -1 properly [view]
  • +
  • fix(doxywizard): fix compiler warning [-Wdeprecated-declarations] [view], [view]
  • +
  • fix(doxywizard): fix compiler warning [-Wdeprecated-enum-enum-conversion] [view]
  • +
  • fix(doxywizard): restore AA_EnableHighDpiScaling for Qt < 6 [view]
  • +
  • fix(translator): change comment alignment [view]
  • +
  • fix(translator): change inherited class [view], [view]
  • +
  • fix(translator): correct aligning [view]
  • +
  • fix(translator): correct typo [view]
  • +
  • fix(translator): remove redundant double quote [view]
  • +
  • fix(translator): replace latin symbol to cyrillic [view]
  • +
  • fix(wizard): fix compiler warning [-Wdeprecated-enum-enum-conversion] [view]
  • +
  • perf(cite): add missing constexpr and noexcept [view]
  • +
  • perf(dir): avoid unnecessary string copy in isEmpty by using const reference [view]
  • +
  • perf(doxyindexer): avoid repeated std::regex construction [view]
  • +
  • perf(doxyindexer): avoid repeated std::string constructions for argv [view]
  • +
+ +

Development improvements

+
    +
  • style: add optional .clang-format configuration [view], +[view]
  • +
  • Some Fixes for MinGW-w64 [view]
  • +
  • build(CMake): turn on link-time optimizations [view]
  • +
  • Explicitly search fmt in the use_sys_fmt=ON case [view]
  • +
+ +

+\endhtmlonly + +\section log_1_14 1.14 Series +\subsection log_1_14_0 Release 1.14.0 +\htmlonly +(release date 24-05-2025) +

+ +

Features

+ + +

Minor incompatibilities

+
    +
  • Due to the HTML output changes customized headers, footers, or stylesheets may require additional tweaks.
  • +
+ +

Bug fixes

+
    +
  • issue #6998 Markdown: Links in ATX headings processed incorrectly? [view]
  • +
  • issue #7522 Source code browser does not distinguish between same-named functions [view], [view]
  • +
  • issue #8731 grouping variables requires an empty line before closing @} [view]
  • +
  • issue #8803 Template class with the same name as existing class gets in the same output file [view], [view]
  • +
  • issue #9437 Avoid empty Markdown page get generated [view], [view], [view], [view]
  • +
  • issue #9437 Add docs about markdown page exceptions [view]
  • +
  • issue #9843 Incomplete call graph when using curly brackets in constructor call (C++) [view]
  • +
  • issue #11001 \copydoc does not respect section hierarchy in table of contents [view]
  • +
  • issue #11111 Overloaded functions are mixed when their arguments have template with object in other namespace [view]
  • +
  • issue #11246 Markdown links to headings are not handled by doxygen like by GitHub or azure [view]
  • +
  • issue #11279 longtabu inside DoxyEnumFields breaks [view]
  • +
  • issue #11299 Regression with HTML_DYNAMIC_MENUS = NO, DISABLE_INDEX = NO, and custom LAYOUT_FILE [view]
  • +
  • issue #11301 Doxygen 1.13.0 ignores visibility="no" for various layout elements [view]
  • +
  • issue #11307 C++ 20 module partition: Links to class members not found [view]
  • +
  • issue #11308 SVG dot graphs have added padding [view]
  • +
  • issue #11309 Markdown blockquotes with lists do not render as expected [view]
  • +
  • issue #11310 "^^" in aliases not proper evaluated [view]
  • +
  • issue #11313 Template class specializations are ignored, which leads to invalid links [view]
  • +
  • issue #11314 "Copy to clipboard" button broken with XHTML [view]
  • +
  • issue #11315 Issue with `@copybrief`, `@ref`, and Markdown links in Markdown table [view], [view]
  • +
  • issue #11320 Documentation fails to build (it freezes, no crash) [view]
  • +
  • issue #11322 C++ 20 Modules: Links to global functions, links to modules [view]
  • +
  • issue #11326 Missing description for a class member of a using-declared class [view]
  • +
  • issue #11330 Snippet trimleft option does not handle newlines well [view], [view]
  • +
  • issue #11333 rebuilding fedora ignition-transport causes abort in isExplicitPage with doxygen 1.13.1 [view]
  • +
  • issue #11342 The code is incorrectly formatted in `@code` command [view]
  • +
  • issue #11344 Link to Examples disappeared from 1.13.0 [view]
  • +
  • issue #11349 Autolink_Test Class incorrect link for constructor [view]
  • +
  • issue #11350 [C#] Documentation Generation Fails for Members Following a Property Getter with Curly Braces in char [view]
  • +
  • issue #11364 Enable SERVER_BASED_SEARCH will break resize handle [view]
  • +
  • issue #11365 Unable to touch scroll with small window width (e.g. on mobile) [view]
  • +
  • issue #11371 Odd behavior of line breaks in double quotes [view]
  • +
  • issue #11373 Code examples in Python docstrings rendered in HTML with wrong indentation beyond 8 spaces [view], [view]
  • +
  • issue #11374 c++ template function definition miss-ref [view]
  • +
  • issue #11377 [in,out] param dir ignored for inline documentation [view]
  • +
  • issue #11387 commit 152ad343c can break verbatim [view]
  • +
  • issue #11393 With EXPAND_ONLY_PREDEF, Doxygen won't expand PREDEFINED macro that's defined in an included header file. [view]
  • +
  • issue #11400 python documentation fails with "::bool" and "::str" warnings [view]
  • +
  • issue #11401 Unexpected html tag `<span>` found within `<dt>` context [view]
  • +
  • issue #11415 doxywizard replaces < and > in aliases with &lt; / &gt; [view]
  • +
  • issue #11416 CMake: Error in doc_internal/CMakeLists.txt:22 Problem configuring file [view], [view]
  • +
  • issue #11419 static keyword appearing as function prefix but not inline [view]
  • +
  • issue #11420 Triple underscores in Markdown mode not supported [view]
  • +
  • issue #11421 `SHOW_GROUPED_MEMB_INC` does not respect `STRIP_FROM_INC_PATH` [view]
  • +
  • issue #11426 Template specialization in file's output point to the main template's documentation instead of the specialization's documentation. [view]
  • +
  • issue #11436 External SVG images not loaded with image-command [view]
  • +
  • issue #11448 Handling of TIMESTAMP in html footer and header [view]
  • +
  • issue #11453 C++17 Deduction Guide with variadic templates [view]
  • +
  • issue #11454 Since version 1.12.0 alias tags cause tables to break [view]
  • +
  • issue #11458 Confusing error message if snippet command misses blockmarkers [view]
  • +
  • issue #11491 [BUG] Space character is not rendered in code block [view]
  • +
  • issue #11504 References not generated when tilde and dot in directory name [view]
  • +
  • issue #11510 enum class value not appearing in the documentation if using std::numeric_limits [view]
  • +
  • issue #11524 XML output is invalid for filenames with "<" or ">" (includedby tag) [view]
  • +
  • issue #11525 Translation error Dutch [view]
  • +
  • issue #11528 `\example{lineno}` does not generate the example [view]
  • +
  • issue #11538 Internal inconsistency: scope for class std::hash<...> not found! [view]
  • +
  • issue #11541 Template specializations get copies of members of the primary template [view]
  • +
  • issue #11543 Doxygen V.1.14 is very slow [view]
  • +
  • issue #11549 Leftover variable in CSS (validation and possible logic issue) [view]
  • +
  • issue #11557 @tableofcontents shows <tt> tags for MarkDown headings with inline code [view], [view], [view], [view]
  • +
  • issue #11563 navtree.js causes Content Security Policy violation with evaluated javascript [view]
  • +
  • issue #11565 subpages named with capital letters are not found by LaTeX [view]
  • +
  • issue #11569 Doxygen might generate invalid tag file, breaking downstream project documentation (no uniquely matching class member found for) [view], [view]
  • +
  • issue #11579 HTML-tables in markdown files with empty lines between the tags not properly rendered [view]
  • +
  • Warning mismatch `#if` / `#endif` from preprocessor [view] and [view]
  • +
  • Fixed issue parsing raw strings [view]
  • +
  • Correctly handle invisible sections in the layout file +[view], +[view], +[view], +[view], +[view], and +[view]
  • +
  • Incorrect substitute pattern in header / footer [view]
  • +
  • CMakeLists.txt: use 64-bit file API on 32-bit linux [view]
  • +
  • Fix wrong links created in search box for external documentations with absolute paths [view]
  • +
  • Consistency of use of `HIDE_COMPOUND_REFERENCE` [view]
  • +
  • Consistent translation of `//!` comments [view]
  • +
  • Tables without any rows result in error in LaTeX [view]
  • +
  • Ignore non-linkable definitions when resolving links [view]
  • +
  • Incorrect field when ampersand in URL [view]
  • +
  • Handling of case sensitive file / directory names [view]
  • +
  • bug 683051 Template parameter cannot be commented inline [view]
  • +
  • String representation at "show configuration" in doxywizard [view]
  • +
  • Section information lost in case of `IMPLICIT_DIR_DOCS=YES` and `Readme.md` [view], and +[view]
  • +
  • Remove defines from values of cpp scoped enums [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Translation german update [view]
  • +
  • Add Linux ARM to CI [view]
  • +
  • Adding debug and version information to doxycommentview [view], [view]
  • +
  • Documentation corrections. [view], and [view]
  • +
  • Update docs of doxycommentview [view], and [view]
  • +
  • Fix XML output for virtual modifiers with trailing return types [view]
  • +
  • Don't repeat the exact same warning messages [view], and [view]
  • +
  • Value mentioned twice with configuration setting `FILE_PATTERNS` [view]
  • +
  • doc: Dependencies for testing [view]
  • +
  • Layout labels on edges [view]
  • +
  • Messages could land in different files [view]
  • +
+ +

Refactoring and cleanup

+
    +
  • Refactoring: replace qstrncmp(s,"literal",7)==0 by literal_at(s,"literal") [view]
  • +
  • Refactoring: use libfmt for messages, warnings and errors [view]
  • +
  • include a conditional usage of c++20 offered by @kaorihinata [view]
  • +
  • Correcting python scripts in respect to style [view]
  • +
  • Prevent code duplication for enum [view], [view]
  • +
  • Incorrect number of doxygen htmlentities [view]
  • +
+ +

+\endhtmlonly + +\section log_1_13 1.13 Series +\subsection log_1_13_2 Release 1.13.2 +\htmlonly +(release date 09-01-2025) +

+ +

Bug fixes

+
    +
  • issue #8803 Template class with the same name as existing class gets in the same output file [view], [view]
  • +
  • issue #11308 SVG dot graphs have added padding [view]
  • +
  • issue #11309 Markdown blockquotes with lists do not render as expected [view]
  • +
  • issue #11313 Template class specializations are ignored, which leads to invalid links [view]
  • +
  • issue #11314 "Copy to clipboard" button broken with XHTML [view]
  • +
  • issue #11320 Documentation fails to build (it freezes, no crash) [view]
  • +
  • issue #11322 C++ 20 Modules: Links to global functions, links to modules [view]
  • +
  • issue #11326 Missing description for a class member of a using-declared class [view]
  • +
  • issue #11330 Snippet trimleft option does not handle newlines well [view], [view]
  • +
  • issue #11333 rebuilding fedora ignition-transport causes abort in isExplicitPage with doxygen 1.13.1 [view]
  • +
  • String representation at "show configuration" in doxywizard [view]
  • +
  • Support for Plus sign code fence directives - GitHub flavor [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Adding debug and version information to doxycommentview [view], [view], [view], and [view]
  • +
+

+\endhtmlonly + +\subsection log_1_13_1 Release 1.13.1 +\htmlonly +(release date 02-01-2025) +

+ +

Minor incompatibilities

+
    +
  • As of doxygen version 1.13.1 and layout version 2.0, Doxygen +will insert defaults for elements that are missing in the user defined layout file. This allows for the introduction of new elements, without having +to update the user defined layout files to make them appear. For older Doxygen +or layout versions, missing elements are still treated as being invisible as before. +
  • +
+ +

Features

+
    +
  • Add WARN_LAYOUT_FILE option to toggle warnings in layout file [view]
  • +
+ +

Bug fixes

+
    +
  • issue #11111 Overloaded functions are mixed when their arguments have template with object in other namespace [view]
  • +
  • issue #11299 Regression with HTML_DYNAMIC_MENUS = NO, DISABLE_INDEX = NO, and custom LAYOUT_FILE [view]
  • +
  • issue #11301 Doxygen 1.13.0 ignores visibility="no" for various layout elements [view], [view], [view], [view], [view], [view], and [view]
  • +
  • issue #11307 C++ 20 module partition: Links to class members not found [view]
  • +
  • Fix compiler error when compiling doxygen in C++20 mode [view]
  • +
  • Silence compiler warning in generated ce_parse.cpp [view]
  • +
  • cmake: Fix flex version check for overriding the register keyword [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Add module part to customize documentation [view]
  • +
  • Documentation corrections. [view]
  • +
+

+\endhtmlonly + +\subsection log_1_13_0 Release 1.13.0 +\htmlonly +(release date 28-12-2024) +

+ +

Minor incompatibilities

+
    +
  • The defaults for DISABLE_INDEX and GENERATE_TREEVIEW have been changed to YES. +If you did not specify them explicitly in your config file and wish to keep +the same output, you need to add +
    +DISABLE_INDEX     = NO
    +GENERATE_TREEVIEW = NO
    +
    +to your config file. +
  • +
  • If you have README.md files in subdirectories, they will be treated as directory descriptions. +You can disable this by setting IMPLICIT_DIR_DOCS=NO in your config file.
  • +
+ +

Features

+
    +
  • issue #11179 Using README.md as directory description via new option IMPLICIT_DIR_DOCS. [view]
  • +
  • issue #11240 Add CSS class for @p command [view]
  • +
  • issue #11245 [Small Feature Request] Add class attribute to the @qualifier command in the HTML output [view]
  • +
  • New option HIDE_UNDOC_NAMESPACES which can be used to hide undocumented namespaces (similar to HIDE_UNDOC_CLASSES) [view] and [view]
  • +
  • New option PLANTUMLFILE_DIRS and command \plantumlfile to include plantuml files (similar to DOT_DIRS, and \dotfile) [view]
  • +
  • New option AUTOLINK_IGNORE_WORDS to specific a set of words that should not be linked [view] and [view]
  • +
  • Update available emojis [view], [view]
  • +
  • Added strip and nostrip options to \include and \snippet to either strip or keep doxygen comments [view]
  • +
  • Added helper script to live edit & view a doxygen comment block, see addon/doxycommentviewer in the repo [view] and [view]
  • +
  • Extending autobrief end characters with ? and ! [view]
  • +
+ +

Bug fixes

+
    +
  • issue #11134 gswin64c.exe not recognized if path added to EXTERNAL_TOOL_PATH [view]
  • +
  • issue #3760 C++ using directives are not understood (Origin: bugzilla #617285) [view], [view], [view], [view]
  • +
  • issue #9921 `HIDE_SCOP_NAMES` and `\ref` [view]
  • +
  • issue #10569 First sentence after defgroup no longer used as brief description [view]
  • +
  • issue #10649 Building failure: ISO C++17 does not allow 'register' storage class specifier [-Wregister] [view], [view]
  • +
  • issue #11012 Doxygen 1.10 and 1.11 not documenting C# enum and its members [view], [view]
  • +
  • issue #11045 copydoc issue with namespace assignment [view]
  • +
  • issue #11065 Doxygen version 1.12.0 failing to differentiate certain overloads when using qtbluetooth.tags tag file [view]
  • +
  • issue #11071 Extra spaces added by GFM special comment parsing cause verbatim blocks [view]
  • +
  • issue #11082 C include format error [view]
  • +
  • issue #11085 Partial specialization of C++ variable template is missing from Doxygen output [view]
  • +
  • issue #11095 Grouping and deriving from a template class leads to "not documented" members. [view]
  • +
  • issue #11102 Objective-C call graph is not shown when self proper is called [view], [view]
  • +
  • issue #11107 PHP heredocs with double quotes stop doxygen [view]
  • +
  • issue #11114 Doxygen 1.12.0 mishandles Markdown section header following `<pre>` HTML markup. [view]
  • +
  • issue #11116 Graphical glitch with lists and text spilling over on a new line. [view]
  • +
  • issue #11120 Python variables erratic documentation [view]
  • +
  • issue #11123 Warning that self is undocumented [view]
  • +
  • issue #11127 copydoc fails to copy the correct function overload for Doxygen 1.9.5 - trunk (7e1860534d163a9d8882ce3ebb620719f74e75eb) [view]
  • +
  • issue #11128 Unknown anchors in code [view], [view]
  • +
  • issue #11130 three-way comparison operator is showing up as "operator" instead of "operator<=>" in related symbols [view]
  • +
  • issue #11135 The anchors don't work [view]
  • +
  • issue #11138 Non-reproducible file names in doxygen output [view]
  • +
  • issue #11144 Template relation doesn't appear on graph [view]
  • +
  • issue #11147 Functions are incorrectly flagged as undocumented if not generating HTML or LaTeX [view]
  • +
  • issue #11156 Enabled AUTOLINK_SUPPORT does not turn enum type names to links [view]
  • +
  • issue #11166 Doc fails on raw string literals [view], [view]
  • +
  • issue #11169 How to copydoc documentation of functions with spaces? [view]
  • +
  • issue #11172 Aliases with embed:rst:leading-asterisk no longer work in Doxygen 1.12 [view], [view], [view], [view]
  • +
  • issue #11184 \ref is unable to resolve references to fields in the documented struct [view]
  • +
  • issue #11205 Building docs for the llvm-project with doxygen takes more than 20 hours [view]
  • +
  • issue #11209 `<kbd>` tag gets rewritten as `<code>` [view]
  • +
  • issue #11210 Support for Numeric code fence directives - GitHub flavor [view]
  • +
  • issue #11217 heap-buffer-overflow WRITE [view]
  • +
  • issue #11225 Improve support for code blocks in included markdown files [view], [view]
  • +
  • issue #11227 Generated documentation for the IDL interface has wrong quotes [view]
  • +
  • issue #11230 Using-declaration with root namespace qualifier is not a hyperlink in source pages [view]
  • +
  • issue #11233 SVG in Markdown is not shown [view]
  • +
  • issue #11237 Xcode project generation failing [view]
  • +
  • issue #11260 Inherited attribute is repeated twice in python [view]
  • +
  • issue #11271 man pages incorrectly convert \" to \' [view]
  • +
  • issue #11273 Using a tilde in the URL of a Markdown image tag breaks the tag if the image is in the IMAGE_PATH [view]
  • +
  • issue #11280 Comments inside a cpp implementation are duplicated. [view]
  • +
  • issue #11281 Alias via using within nested namespace results in incorrect symbol name. [view]
  • +
  • issue #11282 getDefsNew fails to find symbols if a filename matches a namespace name [view]
  • +
  • issue #11283 C++ 20 Concepts not found in search when inside a namespace [view]
  • +
  • bug 432893 handling `*` in ODL properties [view]
  • +
  • bug 729344 Ignoring an items when / or \ is placed after \addindex [view]
  • +
  • Coverity bug 20240822 (1) [view]
  • +
  • Coverity bug 20240822 (2) [view]
  • +
  • Avoid showing internal representation of anonymous scopes in warnings [view]
  • +
  • Improve showing import relations for C++20 modules [view]
  • +
  • Don't attempt to merge typedefs from different scopes. [view]
  • +
  • Compilation error under Cygwin on language.cpp: "too many sections" [view]
  • +
  • Define defined in multiple, nested, files [view]
  • +
  • Detect escaped `\cond` command at end of line [view]
  • +
  • Don't convert CMake environment variables when using `-x_noenv` option [view]
  • +
  • Don't give timing information with options version / help options [view]
  • +
  • Fix case where <= or >= or <=> inside template argument would be interpreted wrongly [view]
  • +
  • Fix out-of-bounds string_view access in Markdown n-/m-dash processing [view]
  • +
  • Fixed a few Doxyfile typos [view]
  • +
  • Fix for missing code->docs links for strong enum value [view]
  • +
  • Fix for wrong code link for enum value [view]
  • +
  • Fix regex logic in scanner.l [view]
  • +
  • Fix source code references for objective-c members [view]
  • +
  • Fixed case giving incorrect 'File ended in the middle of a comment block' warning [view]
  • +
  • Fixed potential invalid memory access when reading code fragments [view]
  • +
  • Fixed some issues handling Java and IDL import statements [view]
  • +
  • Formula doesn't render due to empty lines. [view]
  • +
  • Handling multiple `<br>` tags [view]
  • +
  • Handling of `#pragma once` [view], [view]
  • +
  • Incorrect handling of spaces before `@page` command [view] and [view]
  • +
  • Incorrect label for enum values [view]
  • +
  • Incorrect marking of enum values in source browser [view]
  • +
  • Incorrect warning in case of Python [view]
  • +
  • Incorrect redirection of debug output from preprocessor [view]
  • +
  • Multiple parameters with underscores and one `\param` command [view]
  • +
  • Output when brief description ends with multiple spaces [view]
  • +
  • Plantuml cache file name [view]
  • +
  • Remove inconsistency regarding whitespace at the end of template declaration [view]
  • +
  • Remove some warnings regarding signed/unsigned mismatch [view]
  • +
  • Remove warnings regarding bison package [view]
  • +
  • Removing compiler warnings [view]
  • +
  • Repeated variable names is code browser [view]
  • +
  • Size of project name when part of "treeview" [view]
  • +
  • Use style `plainnat` with `natbib` package [view]
  • +
  • Warning in case of double specification of cite name [view]
  • +
  • Warning with not closed stylchange [view]
  • +
  • XSD warnings [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Update translator_gr.h [view]
  • +
  • Updated translator_ru.h to match updates from 1.8.15 to 1.11.0 [view]
  • +
  • Spelling corrections [view], [view], [view], [view], [view], [view]
  • +
  • Better warning in case of an internal error of a layout element [view]
  • +
+ +

Refactoring and cleanup

+
    +
  • Refactoring PR #11290 a bit [view]
  • +
  • Refactoring: Replace DeepCopyUniquePtr by std::optional [view]
  • +
  • Refactoring: inline internal IMPL class in classdef.cpp [view]
  • +
  • Refactoring: make Token behave as an object [view]
  • +
  • Refactoring: renames for better naming consistency with other classes [view]
  • +
  • Refactoring: replace insert(std::make_pair(...)) by emplace() [view]
  • +
  • update TinyDeflate.hh to get fix clang 19.0 [view], [view], [view] and [view]
  • +
  • Adjust to libfmt-11 changes [view]
  • +
  • Fix for coverity warning about dead code in memberdef.cpp (CID 218000) [view]
  • +
  • Fix for coverity warning about dead code in memberdef.cpp (CID 320261) [view]
  • +
  • Coverity warning [view]
  • +
  • Replaced -t_notime by -t_time and make -t behave as -t_notime did before [view]
  • +
  • Optimize handling trimleft in code fragments [view]
  • +
  • Optimize stripping comment from code fragments [view]
  • +
  • Cleanup unused lexer definitions [view]
  • +
  • Consistency enum class CommandType / HtmlTagType [view]
  • +
+ +

+\endhtmlonly + +\section log_1_12 1.12 Series +\subsection log_1_12_0 Release 1.12.0 +\htmlonly +(release date 07-08-2024) +

+ +

Features

+
    +
  • Adding "engine" chen and chronology for plantuml [view]
  • +
  • Also allow backticks to be combined with emphasis in Markdown [view]
  • +
+ +

Bug fixes

+
    +
  • issue #5661 Inheritance by non-documented classes shows the name of the undocumented class (Origin: bugzilla #738840) [view]
  • +
  • issue #5813 Protected inherited members not documented in derived class if protected functions in base class is not documented (Origin: bugzilla #751453) [view]
  • +
  • issue #6473 Plantuml parse error [view], [view], [view], [view]
  • +
  • issue #8256 Possible to get enum values in the Enumeration Type documentation [view], [view]
  • +
  • issue #8900 markdown: account for punctuation chars before emphasis [view]
  • +
  • issue #8902 markdown: account for quotation marks before emphasis [view]
  • +
  • issue #10606 include command via ALIASES does not work anymore (snippetdoc) [view] and [view]
  • +
  • issue #10696 markdown: allow italicize parentheses [view]
  • +
  • issue #10835 Two relates group generates warning: found multiple \relates, \relatesalso or \memberof commands in a comment block, using last definition [view]
  • +
  • issue #10866 Exiting with exit code 1, no warnings, no errors when executed on GitHub Action [view]
  • +
  • issue #10878 semi-colon after a formula causes error [view]
  • +
  • issue #10897 Incorrect warning about Objective-C Category method not being documented [view], [view]
  • +
  • issue #10902 Commands defined via ALIASES tag have an extra space after value [view]
  • +
  • issue #10902 Improve Markdown Support - blockquotes with special markers parse wrongly [view] and [view]
  • +
  • issue #10904 Topics page lists modules but no descriptions [view]
  • +
  • issue #10906 Unable to resolve reference to 'some/path/README.md' for \ref command [view]
  • +
  • issue #10907 Cite command creates case sensitivity on name [view]
  • +
  • issue #10915 base class function in code block no longer linked if the code block contains a derived class definition calling the base class function in Doxygen 1.9.5 or later [view]
  • +
  • issue #10922 Doxywizard Font Size Rendering Problem on 4k HiRes Monitors [view]
  • +
  • issue #10928 Setting JAVACC_CHAR_TYPE [view]
  • +
  • issue #10932 message: [generated]:8: warning: invalid argument for command '\iline' [view]
  • +
  • issue #10935 \snippet{doc} tag in Doxygen v1.11 adds incorrect paragraph with a break before snippet text [view], [view], [view], [view], [view]
  • +
  • issue #10937 unexpected token TK_COMMAND_BS as the argument of `\ifile` [view], [view]
  • +
  • issue #10949 Doxygen replaces # by :: [view]
  • +
  • issue #10950 ASSERT(0) in docnode.cpp [view], [view], [view]
  • +
  • issue #10951 Computing class inheritance relations -> segmentation fault when run against vulkan-hpp headers [view]
  • +
  • issue #10952 Bad namespace handling with 'using namespace::member' syntax [view]
  • +
  • issue #10953 Java Package Lists does not include nested packages [view]
  • +
  • issue #10955 HTML comment affects output [view]
  • +
  • issue #10956 Bad indent for list embedded in <dd> in man output [view]
  • +
  • issue #10959 Capability to render GitHub flavor Markdown comments [view]
  • +
  • issue #10959 Support of special HTML comments [view]
  • +
  • issue #10960 Variadic macro expansion with comments [view]
  • +
  • issue #10962 std:: hypertext link if a class documents std() function [view], [view]
  • +
  • issue #10964 C++ specialization: warning: explicit link request could not be resolved. [view]
  • +
  • issue #10969 \ref command does not respect "text" field for template classes in Doxygen 1.10.0 - trunk [view]
  • +
  • issue #10970 Improvement of end of paragraph detection [view]
  • +
  • issue #10972 Derived class documentation no longer contains related functions using protected inheritance on Doxygen trunk [view]
  • +
  • issue #10973 attribute is repeated twice in python [view]
  • +
  • issue #10977 dontinclude pattern find itself [view]
  • +
  • issue #10987 Doxygen 1.11.0 for macOS reports 'sh: latex: command not found' [view], [view]
  • +
  • issue #10989 Incorrect linking in C++ code [view]
  • +
  • issue #10989 bison: error: '%name-prefix' and '%define api.prefix' cannot be used together [view]
  • +
  • issue #10991 @snippetdoc not support multi lines [view]
  • +
  • issue #10994 Automatic link for macro defined in different header file no longer generated [view]
  • +
  • issue #10995 Build issue: Build picks up wrong git info [view]
  • +
  • issue #10997 How to tell Doxygen to process an #include within a class definition [view], [view]
  • +
  • issue #11000 Cannot read properties of undefined (reading 'css') (resize.js:64) [view]
  • +
  • issue #11006 Unable to get property 'style' of undefined or null reference [view]
  • +
  • issue #11010 Automatic link to typedef no longer generated if typedef declared twice [view]
  • +
  • issue #11013 compound.xsd is missing types for some elements [view]
  • +
  • issue #11015 Faulty xml generated when markdown header is too deep [view]
  • +
  • issue #11015 In Doxygen 1.11.0, Include Graphs are not made for Java [view]
  • +
  • issue #11016 Faulty xml generated when markdown header is too deep. [view], [view], [view], [view]
  • +
  • issue #11031 extra WS in @brief docu block break layout [view]
  • +
  • issue #11033 Illegal command '\ifile' found as part of a title section [view]
  • +
  • issue #11036 The body of a macro entirely defined on 1 line gets sucked into the synopsis [view]
  • +
  • issue #11056 HTML: Interactive SVG "viewHeight" is not defined [view]
  • +
  • Prevent extra scrollbar in HTML output when using Firefox [view] and [view]
  • +
  • xmllint error in combination with addindex command [view]
  • +
  • Files stayed in RTF directory [view]
  • +
  • Remove temporary file [view]
  • +
  • Warning in case of `0.` as start of a list. [view]
  • +
  • With the simple file ``` /// \file ``` and setting ``` GENERATE_RTF=YES ``` we get in the messages a number of times the message: ``` Style 'Heading6' redefines \s5. ``` [view]
  • +
  • Python class variables type and initial value [view]
  • +
  • Fix buffer overflow in Markdown parser [view]
  • +
  • Add code coloring for Fortran RANK statement [view]
  • +
  • Fixed a number of coverity warnings [view] and [view]
  • +
  • [fix] multithreading issue with ModuleManager [view]
  • +
  • fix: use correct option name with system sqlite3 [view]
  • +
  • Missing filename for tag warnings [view]
  • +
  • Stop showing classes and concepts in namespace list [view]
  • +
+ +

Improved user feedback and documentation

+
    +
  • Document all possible values of dir attribute in param command [view], [view] and [view]
  • +
  • made row of asterisks same length to align them [view], [view]
  • +
  • Spelling correction [view] and [view]
  • +
  • Redirecting link corrected [view]
  • +
  • Debug output for layout file [view]
  • +
  • Enable additional asserts in debug builds [view]
  • +
  • Setup for doxygen documentation [view]
  • +
+ +

Refactoring and cleanup

+
    +
  • Changed debug mode define to avoid linker issues on macOS [view]
  • +
  • Selection possibility of values from an enum (class) by means of `is_any_of` [view]
  • +
  • Use CMake's FindIconv (see #10887) [view]
  • +
  • Improve readability of codebase [view]
  • +
  • Cleanup `Doxygen::terminating` [view]
  • +
  • Better type safety of enums [view] and [view]
  • +
  • Refactoring: Remove mutable from DotNode's m_written member variable [view]
  • +
  • Refactoring: Remove unneeded mutable for MemberDefImpl::m_annScope [view]
  • +
  • Refactoring: reduce lock contention when adding cross references [view]
  • +
  • Refactoring: reduce lock contention when reading code fragments [view]
  • +
  • Refactoring: reduce lock contention when writing tooltips [view]
  • +
  • Refactoring: remove avoidable const_cast's [view]
  • +
  • Refactoring: remove mutable from DefinitionAliasImpl [view]
  • +
  • Refactoring: remove unneeded const_cast's [view]
  • +
  • Refactoring: remove unused resolveAlias() method from ClassDef/ConceptDef [view]
  • +
  • Refactoring: Make MemberTypeList a class instead of enum with bit mask [view]
  • +
  • Prepare CMakeLists.txt for using mutrace (Linux only) [view]
  • +
+ +

+\endhtmlonly + \section log_1_11 1.11 Series \subsection log_1_11_0 Release 1.11.0 \htmlonly @@ -14,7 +788,7 @@

  • Added 'raise' and 'prefix' options to @include{doc} [view], [view]
  • Support link / endlink command in section title [view]
  • Add support for @subparagraph and @subsubparagraph [view], [view], [view], [view], [view]
  • -
  • Translation updates for German/Greek/Polish/Portuegse/Dutch/Chinese [view], [view], [view], [view], [view], [view], [view], [view], [view], [view], [view], [view]
  • +
  • Translation updates for German/Greek/Polish/Portuguese/Dutch/Chinese [view], [view], [view], [view], [view], [view], [view], [view], [view], [view], [view], [view]
  • doxyapp --locate list all overloads by including arguments [view]
  • Adding support for "engine" files for plantuml [view]
  • Show emoji in HTML treeview [view], [view]
  • @@ -100,13 +874,13 @@
  • Adding sub links to doxygen_crawl file. [view], [view]
  • Always set Compiled file for HTML help compiler [view]
  • Block command in section title [view]
  • -
  • Clang assisited parsing: correctly parse C++ headers as headers [view]
  • +
  • Clang assisted parsing: correctly parse C++ headers as headers [view]
  • Code folding didn't work when CLANG_ASSISTED_PARSING was enabled [view]
  • Code sections in Perl documentation not expandable anymore [view]
  • Don't import links from tags into crawl file [view]
  • Fix clang-assisted parsing with clang >= 14 [view]
  • Fix discarding of Slice types during file index generation. [view]
  • -
  • Fix for LaTeX table overlaping footer issue [view]
  • +
  • Fix for LaTeX table overlapping footer issue [view]
  • Fix invalid generated link when using Markdown GitHub style link to a section in another file [view]
  • Fix issue where ALIAS argument was split over multiple lines [view], [view], [view], [view]
  • Fix potential crash if section titles contain unsupported markup [view], [view]
  • @@ -128,7 +902,7 @@
  • QT help problems with tag files [view], [view]
  • Single line snippet didn't show [view]
  • Support unicode characters in referenced file names [view], [view]
  • -
  • Waring with ingroup command at end of alias [view]
  • +
  • Warning with ingroup command at end of alias [view]
  • Warning for Fortran on \code command [view]
  • Warnings due to multiple "sectioning" commands inside an \if type construct [view], [view], [view]
  • When having markdown code like: ~~~ [view]
  • @@ -158,7 +932,7 @@
  • Consistency of arguments with other translator functions [view]
  • Flexibility for trDocumentation in respect to the ProjectName [view]
  • Make SrcLangExt an enum class [view], [view], [view]
  • -
  • Performance optimisations for functions on the critical path [view]
  • +
  • Performance optimizations for functions on the critical path [view]
  • Refactor contexts_t and contexts in htmldocvisitor.cpp [view], [view]
  • Refactor implementation [view], [view]
  • Refactoring: Added ENABLE_CLANG_TIDY option and added rule of 0 or rule of 5 special members [view], [view], [view], [view], [view], [view], [view], [view], [view]
  • Cleanup unreachable code [view]
  • @@ -236,8 +1010,8 @@
  • issue #10466 Markdown: inline statements in page headings are escaped and render as text [view]
  • issue #10472 Symbols within a namespace after a heavily #ifdef class are omitted in HTML output [view]
  • issue #10473 doxygen hangs forever after "Building directory list..." [view], [view], [view]
  • -
  • issue #10474 doxygen fails to render /some/ MarkDown links to local files with relative path [view]
  • -
  • issue #10475 doxygen does not correctly render link content in MarkDown file [view]
  • +
  • issue #10474 doxygen fails to render /some/ Markdown links to local files with relative path [view]
  • +
  • issue #10475 doxygen does not correctly render link content in Markdown file [view]
  • issue #10485 Parsing error with C++20 requires clause if at the end of the function declaration [view]
  • issue #10498 #cmakedefine01 macros are not documented [view], [view]
  • issue #10499 Child class with template parameter named differently than parent: unqualified base class in tagfile [view]
  • @@ -378,7 +1152,7 @@
  • issue #3476 The encoding of the layout file is not taken into account [view]
  • issue #4134 C# abstract/sealed class attributes are not stored in XML output [view]
  • issue #7580 No autolink for template functions [view]
  • -
  • issue #8393 @Endcond not behaving as expected (missing warnings, missing output) [view]
  • +
  • issue #8393 @endcond not behaving as expected (missing warnings, missing output) [view]
  • issue #9964 [VHDL] - type and name of type in type-generic entity are swapped [view]
  • issue #10056 [1.9.7] Markdown Header ID ignored in subdirectories [view]
  • issue #10076 \include and Python [view]
  • @@ -500,7 +1274,7 @@
  • clean: replace 0 with nullptr in qstrcpy() implementation for better readability [view]
  • style: adjust indentation in qstricmp() and qstrnicmp() [view]
  • style: prefer nullptr to 0 in QCString::find() [view]
  • -
  • style: remove trailing spaces in github action workflow file [view]
  • +
  • style: remove trailing spaces in GitHub Action workflow file [view]
  • style: use 2 spaces in testing/CMakeLists.txt [view]
  • Silence 2 coverity warnings [view]
  • Refactoring: Add 'override' to virtual methods of symbol definitions [view]
  • @@ -862,8 +1636,8 @@ href="https://github.com/doxygen/doxygen/commit/2073b3b376df7307cc45ea4e007dfd59%20%3Cli%3EFixed%20main%20menu%20and%20submenu%20items%20for%20Fortran%20and%20VHDL%20in%20context%20of%20other%20languages.%20[%3Ca%20href="https://github.com/doxygen/doxygen/commit/6fa5b54dfd915ca1316b81e7094447d017e6efa1">view]
  • Incorrect documentation for the command \fileinfo [view]
  • QHP output requires that HTML output is generated [view]
  • -
  • Dutch and czech localization update to "new since 1.9.6. [view]
  • -
  • Portuguese translators updated to 1.9.6 [view] and Portuguese translators updated to 1.9.6. [view]
  • +
  • Dutch and Czech localization update to "new since 1.9.6". [view]
  • +
  • Portuguese translators updated to 1.9.6 [view] and [view]
  • Update translator_fr.h [view], [view], [view], [view], [view], [view]
  • Added translation specialization for VHDL in the trMemberFunctionDocumentation() method. [view]
  • @@ -1069,7 +1843,7 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using issue #9007 Using DOT_PATH with a symlink for dot does not always work [view], and [view]
  • issue #9027 SEARCHDATA_FILE (searchdata.xml) keyword elements blank [view]
  • issue #9028 DoxyVerb environment should terminate the previous paragraph before changing paragraph formatting [view]
  • -
  • issue #9045 wrong grammar/spelling in german html Output [view]
  • +
  • issue #9045 wrong grammar/spelling in German html output [view]
  • issue #9051 Wrong "Related page" generating in 1.9.3 (works good in 1.9.1!) from *.md files [view], [view]
  • issue #9054 Feature Request: Doxywizard: Customize Doxyfile Line Endings [view]
  • issue #9055 Objective C method / property attribute decoration confuses parsing [view]
  • @@ -1194,7 +1968,7 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using Make boolean representation mapping part of ConfigValues::Info [view]
  • Making Windows executable large address aware [view]
  • Making tag information better available [view], [view]
  • -
  • Minor optimisation [view]
  • +
  • Minor optimization [view]
  • Missing <VERSION> in description of doxyparse [view]
  • Missing part of output due to wrong emoji command [view]
  • Modernize LANGUAGE.HOWTO [view]
  • @@ -1259,7 +2033,7 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using bug_772405 *_AUTOBRIEF=YES gobbles end of paragraph from brief [view]
  • bug_774111 GENERATE_LEGEND with svg graphs [view], [view]
  • bug_774167 Left nav pane [view]
  • -
  • bug_791832 [view]
  • +
  • bug_791832 \static command not working [view]
  • bug_792690 Spaces MAKEINDEX_CMD_NAME don't work the same as other config options [view]
  • bug_796673 doxygen markdown engine disallow parentheses in image title [view]
  • issue #7434 Error in LaTeX output of doxygen documentation of VHDL [view]
  • @@ -1275,7 +2049,7 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using issue #8743 <tt> misinterpreted [view], [view]
  • issue #8747 Improve output with WARN_AS_ERROR = FAIL_ON_WARNINGS [view], [view]
  • issue #8750 Crashes with 1.9.2 on Ubuntu 21.04 [view], [view]
  • -
  • issue #8752 Generated filenames depend on undefined behaviour - doxygen output not reproducible [view]
  • +
  • issue #8752 Generated filenames depend on undefined behavior - doxygen output not reproducible [view]
  • issue #8753 qhp file corrupt when subsection with no parent section [view]
  • issue #8755 :: lost in nested names when using markdown [view]
  • issue #8757 Segfault when using external search engine [view]
  • @@ -1304,7 +2078,8 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using issue #8905 Documentation: WARN_NO_PARAMDOC not disabled by EXTRACT_ALL? [view], [view]
  • issue #8909 Build error with git head GCC (version 12, Nov 2021) [view]
  • issue #8911 Undocumented function with C++ requires clause [view]
  • -
  • issue #8914 Double Square Bracket – somewhere – stop Doxygen [view]
  • +
  • issue #8914Double Square Bracket – somewhere – stops Doxygen [view]
  • issue #8918 \emoji does not support emoji with ZWJ [view]
  • issue #8921 Overloaded function (with templates as change) is not displayed in html page [view]
  • issue #8924 Horizontal scroll bar missing in HTML for wide class="dotgraph" objects [view]
  • @@ -1336,7 +2111,7 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using Incorrect handling of JAVA style code statement in preprocessor [view]
  • Incorrect linecount in case of JAVADOC_AUTOBRIEF set [view]
  • Incorrect markdown links. [view]
  • -
  • Incorrect warning regarding SQLITE3 seyyttings [view]
  • +
  • Incorrect warning regarding SQLITE3 settings [view]
  • Incorrect warnings due to WARN_IF_DOC_ERROR=NO [view]
  • Incorrect xml tags in source code listing (XML output) [view]
  • Doxygen stops on invalid namespace name [view]
  • @@ -1362,9 +2137,9 @@ customize the way dot images are rendered. When upgrading the Doxyfile (using Avoid tampering with special commands while doing markdown parsing [view] and [view]
  • Adding HTML tag <cite> [view]
  • Adding engine to xml output for plantuml [view]
  • -
  • Czech localization update, added all missing czech localization strings up to 1.9.3 [view]
  • -
  • translate chinese upto version 1.9.2 [view]
  • -
  • Updated swedish translation to 1.9.2 [view]
  • +
  • Czech localization update, added all missing Czech localization strings up to 1.9.3 [view]
  • +
  • translate Chinese upto version 1.9.2 [view]
  • +
  • Updated Swedish translation to 1.9.2 [view]
  • Greek translation updated to 1.9.2. [view]
  • Portuguese translators updated to 1.9.3. [view]
  • UTF-8 characters in email address [view]
  • @@ -1473,7 +2248,7 @@ The same functionality can now be achieved using HAVE_DOT and CLASS_GRAPH. Old s
  • issue #8476: Call graph regression: Some parts are mixed up [view], [view]
  • issue #8474: Crash: double free or corruption (out) [view]
  • issue #8471: doxygen 1.9.1 segmentation fault when generating libstdc++ documentation [view]
  • -
  • issue #8465: Parens in return type template are dropped [view]
  • +
  • issue #8465: Parentheses in return type template are dropped [view]
  • issue #8448: [c++] unable to expand recursive macro [view]
  • issue #8426: Incorrect line number from tag file [view]
  • issue #8425: Would like a way to sort class list but not brief member list [view]
  • @@ -1516,7 +2291,7 @@ The same functionality can now be achieved using HAVE_DOT and CLASS_GRAPH. Old s
  • Doxygen was confused by class keyword in 'using A = class B' [view]
  • Equation reference for formulas missing for HTML / Docbook / RTF [view]
  • Strange HTML tab title in case of a page without title [view]
  • -
  • Fix bug linking C++ anonymouus workspace [view]
  • +
  • Fix bug linking C++ anonymous workspace [view]
  • Self recursive Fortran functions did not show a self arrow in the callgraph [view]
  • Self recursive functions did not show a self arrow in the callgraph [view]
  • Incorrect DOXYFILE_ENCODING upon update [view]
  • @@ -1530,7 +2305,7 @@ The same functionality can now be achieved using HAVE_DOT and CLASS_GRAPH. Old s
  • Add config option WARN_IF_INCOMPLETE_DOC [view]
  • Add config options GENERATE_SQLITE3, SQLITE3_OUTPUT and SQLITE3_RECREATE_DB to better control the sqlite3 output. [view]
  • Add config option MATHJAX_VERSION to support both MathJax 2.x and 3.x [view]
  • -
  • Allow more fine tuning of semanitic syntax highlighting via CSS classes [view], +
  • Allow more fine tuning of semantic syntax highlighting via CSS classes [view], [view], [view]
  • Use language name to get code coloring [view]
  • Extra settings for MathJax V3 [view]
  • @@ -1570,13 +2345,13 @@ The same functionality can now be achieved using HAVE_DOT and CLASS_GRAPH. Old s
  • Empty / obsolete file removed [view]
  • Empty class definition. [view]
  • Enable parallel processing of the "Generating file sources..." step. [view]
  • -
  • Environment variabelen in changelog [view]
  • +
  • Environment variables in changelog [view]
  • Error messages on ASSERTS [view]
  • Extended doxygen version information [view]
  • Extending startuml with extra figure types [view], [view]
  • Extension during mapping not correctly replaced. [view]
  • Fix deadlock when using WARN_AS_ERROR = YES. [view]
  • -
  • Fix difference in behaviour between QDir::exists and Dir::exist() [view]
  • +
  • Fix difference in behavior between QDir::exists and Dir::exist() [view]
  • Fix issue with test 055 on Cygwin [view]
  • Fix issues caused by QCString::rawData and QCString::operator[] [view]
  • Fix macro redefinition warning for windows build [view]
  • @@ -1837,15 +2612,15 @@ and

    Refactoring and cleanup

    • Embed MemberGroup objects directly in their container [view]
    • -
    • Modernise client side part of searchindex [view]
    • +
    • Modernize client side part of searchindex [view]
    • avoid copying MemberLists by not embedding them directly [view]
    • change MemberGroupSDict to MemberGroupList [view]
    • change type of m_cache [view]
    • change type of vhdlSummaryTitles from SDict<QCString> to StringSet [view]
    • -
    • moderize Doxygen::dirRelations [view]
    • -
    • modernise Doxygen::clangUsrMap [view]
    • -
    • modernise MemberGroupList [view]
    • -
    • modernise member indices [view]
    • +
    • modernize Doxygen::dirRelations [view]
    • +
    • modernize Doxygen::clangUsrMap [view]
    • +
    • modernize MemberGroupList [view]
    • +
    • modernize member indices [view]
    • modernize getMemberLists() [view]
    • remove SIntDict and SIntList (no longer used) [view]
    • remove unused ClassList [view]
    • @@ -2017,7 +2792,7 @@ and
    • Renamed EXTRACT_ANON_ARGUMENTS to RESOLVE_UNNAMED_PARAMS and enabled it by default [view]
    • Silently ignoring unexpected characters in configuration [view], [view]
    • Simplified escaping for latex URLs [view]
    • -
    • Some further simplifications and modernisations [view]
    • +
    • Some further simplifications and modernizations [view]
    • Some tweaks & fixes [view]
    • Space around images in LaTeX output is sometimes a bit large [view]
    • Split into separate rules [view]
    • @@ -2063,9 +2838,9 @@ and
    • Fix some issues in reporting warnings for Doxywizard's config parser [view]
    • Introduce ScopedTypeVariant [view]
    • Introduce immutable and mutable interfaces [view]
    • -
    • Modernise DotGfxHierarchyTable [view]
    • -
    • Modernise diagram.h/diagram.cpp [view]
    • -
    • Modernise BaseClassList [view]
    • +
    • Modernize DotGfxHierarchyTable [view]
    • +
    • Modernize diagram.h/diagram.cpp [view]
    • +
    • Modernize BaseClassList [view]
    • OutputList & OutputGen [view]
    • Re-added sorting for namespaces [view]
    • Remove unused DefinitionIntf and DefinitionList classes [view]
    • @@ -2079,7 +2854,7 @@ and
    • Making sqlcode.l reentrant [view]
    • Making vhdlcode.l reentrant [view]
    • Making xmlcode.l reentrant [view]
    • -
    • Modernised the remaining containers in code.l [view]
    • +
    • Modernized the remaining containers in code.l [view]
    • Modernize Doxygen::symbolMap [view]
    • Modernize TooltipManager class and source reference lists [view]
    • Modernize class index [view]
    • @@ -2512,7 +3287,7 @@ and
    • Small spelling correction in portable.cpp [view]
    • Specifying filename in preprocessor debug output [view]
    • Unknown configuration enum values [view]
    • -
    • Updated test 024 to better test spacing behaviour. [view]
    • +
    • Updated test 024 to better test spacing behavior. [view]
    • Warnings in case of a VHDL error [view]
    • commentscan.l: replace QStack by std::stack [view]
    • fixed some parser bugs,make parser ready for javacc 7.0.5 [view]
    • @@ -3086,7 +3861,7 @@ href="https://github.com/doxygen/doxygen/commit/9d83d43f0d8b7058e2f89371a6ab276c%20%3Cli%3Eremove%20PLANTUML_RUN_FAST%20because%20FAST%20is%20default.%20[%3Ca%20href="https://github.com/doxygen/doxygen/commit/51bba9d513b22bd92d90127657b76f51d4555fa8">view]
    • secref command output shows in 1 column (HTML) [view]
    • source arrangement [view]
    • -
    • spelling error: suported -> supported [view]
    • +
    • spelling error: supported -> supported [view]
    • sqlcode.l: generate a reentrant scanner [view]
    • take doc group out of commentscan.l [view]
    • testing: add a test for TOC levels in the XML output. [view]
    • @@ -3521,7 +4296,7 @@ href="https://github.com/doxygen/doxygen/commit/a697caadf1912d0d74faa208f4cff887%20%3Cli%3EBug%20%3Ca%20href="https://github.com/doxygen/doxygen/issues/5468">5468 - (UnFriendlyTemplate) Spurious warning when documenting friend template [view]
    • Bug 5525 - parser misinterpreting fortran [view]
    • Bug 5724 - Duplicate attribute (target="_top" target="_top") generated in .SVG files [view]
    • -
    • Bug 6128 - Usage of underscore's in parameter names [view]
    • +
    • Bug 6128 - Usage of underscores in parameter names [view]
    • Bug 6135 - [1.8.13 Regression] Segfault building the breathe docs [view]
    • Bug 6137 - XML Parsing Error for operator<< methods when outputting to XHTML [view]
    • Bug 6139 - Menu does not work without JavaScript [view]
    • @@ -3887,7 +4662,7 @@ href="https://github.com/doxygen/doxygen/commit/a697caadf1912d0d74faa208f4cff887%20%3Cli%3EBug%20%3Ca%20href="https://github.com/doxygen/doxygen/issues/5808">5808 - Doxygen don't support longer key in bibtex [view]
    • Bug 5809 - \cite still rejects valid BibTeX keys [view]
    • Bug 5821 - using plantuml cause a popup "openwith" windows when calling java.exe [view]
    • -
    • Bug 5826 - PATCH: Honour SOURCE_DATE_EPOCH environment variable for reproducible output [view]
    • +
    • Bug 5826 - PATCH: Honor SOURCE_DATE_EPOCH environment variable for reproducible output [view]
    • Bug 5830 - XML not documenting a class in python [view]
    • Bug 5831 - XML empty <argsstring/> in python [view]
    • Bug 5832 - last entry missing in a @name group of typedefs [view]
    • @@ -4806,7 +5581,7 @@ href="https://github.com/doxygen/doxygen/commit/a697caadf1912d0d74faa208f4cff887%20%3Cli%3E%20doc/language.doc%20generated%20from%20the%20updated%20sources%20(bgcolored)%3C/li%3E%20%3Cli%3E%20doc/language.tpl%20--%20UTF-8%20reflected%20in%20the%20langhowto%20template%3C/li%3E%20%3Cli%3E%20doc/language.tpl%20--%20trailing%20spaces%20removed%3C/li%3E-%3Cli%3E%20doc/translator.py%20--%20coloured%20status%20in%20HTML%3C/li%3E+%3Cli%3E%20doc/translator.py%20--%20colored%20status%20in%20HTML%3C/li%3E%20%3Cli%3E%20doxygen%20/**%20style%20creates%20spurious%20code%20blocks%20with%20markdown%20enabled%3C/li%3E%20%3Cli%3E%20doxygen%20version%201.8.5%20throws%20many"Internal Inconsistency" errors when parsing .idl files
    • doxygen.sty.h was not ignored and not included/generated properly
    • @@ -5222,7 +5997,7 @@ href="https://github.com/doxygen/doxygen/commit/a697caadf1912d0d74faa208f4cff887%20%20%20%20%20%20%20%20when%20using%20C++11%20style%20enums.%3C/li%3E%20%3Cli%3E%20%20%20id%20%3Ca%20href="https://github.com/doxygen/doxygen/issues/4920">4920: Even though HIDE_UNDOC_MEMBERS was enabled, the navigation still showed undocumented members. -
    • id 4921: Fixed back button behaviour when using the navigation tree.
    • +
    • id 4921: Fixed back button behavior when using the navigation tree.
    • id 4927: Added anchors to refs in the index.qhp TOC.
    • id : Added XML declaration to index.qhp TOC.
    • id 4932: When a class and its base class has the same nested class, @@ -6589,7 +7364,7 @@ make sure you add the following:
    • id 3627, 3648: The title of pages whose label had an underscore was not shown
    • id 3631: Include guard not starting with #ifndef SOME_GUARD_H were not - recognised as such.
    • + recognized as such.
    • id 3632: Setting SEARCHENGINE to YES and GENERATE_HTML to NO caused error that search results directory could not be created.
    • id 3635, 3655: typedef'ed enums or struct with the same as the @@ -6696,7 +7471,7 @@ make sure you add the following: was enabled.
    • id 3549: Scope was not hidden for members in the todo list even though HIDE_SCOPE_NAMES was set to YES.
    • -
    • id 3562: Struct variable with explicit struct keyword got labelled +
    • id 3562: Struct variable with explicit struct keyword got labeled with [read] attribute.
    • id 3545: PHP was not parsed properly when it appeared in a <script language="php"> section.
    • @@ -11692,7 +12467,7 @@ make sure you add the following: option: INCLUDE_FILE_PATTERNS. This tag can be used to set the file patterns for the include files (if left empty the FILE_PATTERNS will be used, which was also the old - behaviour). + behavior).
    • Added a couple of commands for kdoc compatibility: @p, @li, @em. Also made @ref a bit less strict.
    • diff --git a/doc/commands.dox b/doc/commands.dox index 82e309fadd3..92b8e533b7c 100644 --- a/doc/commands.dox +++ b/doc/commands.dox @@ -236,6 +236,7 @@ documentation: \refitem cmdtodo \\todo \refitem cmdtparam \\tparam \refitem cmdtypedef \\typedef +\refitem cmdplantumlfile \\plantumlfile \refitem cmdunion \\union \refitem cmduntil \\until \refitem cmdvar \\var @@ -260,6 +261,8 @@ documentation: \refitem cmdperc \\\% \refitem cmdquot \\\" \refitem cmdchardot \\\. +\refitem cmdquest \\\? +\refitem cmdexclam \\\! \refitem cmddcolon \:: \refitem cmdpipe \\| \refitem cmdndash \\\-- @@ -2474,7 +2477,7 @@ Commands to create links \sa section \ref cmdref "\\ref".
      -\section cmdcite \\cite \ +\section cmdcite \\cite['{'[option]'}'] \ \addindex \\cite Adds a bibliographic reference in the text and in the list of bibliographic @@ -2485,6 +2488,18 @@ Commands to create links output formats a fixed representation is used. Note that using this command requires the \c bibtex tool to be present in the search path. + There are a number of options possible: + - `number`, `shortauthor`, `year`, these options are mutually exclusive, + in case none of these is specified `number` is assumed. + - `number` create a numerical reference + - `shortauthor` just the surname of the first author is given, and in case + multiple authors are present the text "et al." is added + - `year`, the year of publication is mentioned (when specified in the bibtex + file. + - `nopar`, no (square) brackets are added + - `nocite`, no link to a citation in the bibliography is made (and the + reference is not added to the bibliography based on this item) +
      \section cmdendlink \\endlink @@ -2729,7 +2744,12 @@ Commands for displaying examples \ref cfg_example_path "EXAMPLE_PATH" tag of Doxygen's configuration file. - You can add option `lineno` to enable line numbers for the included code if desired. + You can add the option `lineno` to enable line numbers for the included code if desired. + + You can add the option `strip` that will always hide any special comments + from the included code, overruling the + \ref cfg_strip_code_comments "STRIP_CODE_COMMENTS" setting, or add the option + `nostrip` to always show the special comments. The class and member declarations and definitions inside the code fragment are 'remembered' during the parsing of the comment block that contained @@ -2798,6 +2818,11 @@ Commands for displaying examples - The `option` `doc` can be used to treat the file as documentation rather than code. - The `option` `local` can be used make Doxygen interpret the code as if it was in the class or namespace in which the include command appears, rather than the global namespace. + - The `option` `strip` can be used to always hide any special comments + from the included code, overruling the + \ref cfg_strip_code_comments "STRIP_CODE_COMMENTS" setting, and option + `nostrip` can be used to always show the special comments. + These options have no effect in combination with the `option` `doc`. When using option `doc`, there is also the option `raise` that can be specified to raise all sections found in the referenced file by a certain amount. For example @@ -2946,6 +2971,11 @@ Commands for displaying examples - The `option` `doc` can be used to treat the file as documentation rather than code. - The `option` `local` can be used make Doxygen interpret the code as if it was in the class or namespace in which the include command appears, rather than the global namespace. + - The `option` `strip` can be used to always hide any special comments + from the included code, overruling the + \ref cfg_strip_code_comments "STRIP_CODE_COMMENTS" setting, and option + `nostrip` can be used to always show the special comments. + These options have no effect in combination with the `option` `doc`. When using option `doc`, there is also the option `raise` that can be specified to raise all sections found in the referenced file by a certain amount. For example @@ -3510,7 +3540,8 @@ class Receiver Not all diagrams can be created with the PlantUML `@startuml` command but need another PlantUML `@start...` command. This will look like `@start` where currently supported are the following ``s: `uml`, `bpm`, `wire`, `dot`, `ditaa`, `salt`, `math`, `latex`, - `gantt`, `mindmap`, `wbs`, `yaml`, `creole`, `json`, `flow`, `board`, `git`, `hcl`, `regex`, `ebnf` and `files`. + `gantt`, `mindmap`, `wbs`, `yaml`, `creole`, `json`, `flow`, `board`, `git`, `hcl`, `regex`, `ebnf`, + `files`, `chen` and `chronology`. By default the `` is `uml`. The `` can be specified as an option. Also the file to write the resulting image to can be specified by means of an option, see the description of the first (optional) argument for details. @@ -3746,6 +3777,30 @@ class Receiver \addindex \\enduml Ends a block that was started with \ref cmdstartuml "\\startuml". +
      +\section cmdplantumlfile \\plantumlfile \ ["caption"] [\=\] + + \addindex \\plantumlfile + Inserts an image made in PlantUml from \ into the documentation. + + The first argument specifies the file name of the image. + Doxygen will look for files in the paths (or files) that you specified + after the \ref cfg_plantumlfile_dirs "PLANTUMLFILE_DIRS" tag. + If the plantuml file is found it will be used as an input file for the plantuml program. + The resulting image will be put into the correct output directory. + If the plantuml file name contains spaces you'll have to put quotes ("...") around it. + + The second argument is optional and can be used to specify the caption + that is displayed below the image. This argument has to be specified + between quotes even if it does not contain any spaces. The quotes are + stripped before the caption is displayed. + + The third argument is also optional and can be used to specify the + width or height of the image. + For a description of the possibilities see the paragraph + \ref image_sizeindicator "Size indication" with the + \ref cmdimage "\\image" command. +
      \section cmdendhtmlonly \\endhtmlonly @@ -3874,8 +3929,8 @@ class Receiver \ref cmdendhtmlonly "\\endhtmlonly" is inserted as-is. When you want to insert a HTML fragment that has block scope like a table or list which should appear outside \..\, this can lead to invalid HTML. - You can use `\htmlonly[block]` to make Doxygen - end the current paragraph and restart it after \\endhtmlonly. + You can use \\htmlonly[block] to make Doxygen + end the current paragraph and restart it after \\endhtmlonly. \note environment variables (like \$(HOME) ) are resolved inside a HTML-only block. @@ -4201,10 +4256,27 @@ class Receiver \addindex \\\. This command writes a dot (`.`) to the output. This can be useful to - prevent ending a brief description when \ref cfg_javadoc_autobrief "JAVADOC_AUTOBRIEF" is enabled + prevent ending a brief description when \ref cfg_javadoc_autobrief "JAVADOC_AUTOBRIEF" + or \ref cfg_qt_autobrief "QT_AUTOBRIEF" is enabled or to prevent starting a numbered list when the dot follows a number at the start of a line. +
      +\section cmdquest \\? + + \addindex \\\? + This command writes a question mark (`?`) to the output. This can be useful to + prevent ending a brief description when \ref cfg_javadoc_autobrief "JAVADOC_AUTOBRIEF" + or \ref cfg_qt_autobrief "QT_AUTOBRIEF" is enabled. + +
      +\section cmdexclam \\! + + \addindex \\\! + This command writes a exclamation mark (`!`) to the output. This can be useful to + prevent ending a brief description when \ref cfg_javadoc_autobrief "JAVADOC_AUTOBRIEF" + or \ref cfg_qt_autobrief "QT_AUTOBRIEF" is enabled. +
      \section cmdeq \\= diff --git a/doc/customize.dox b/doc/customize.dox index d6e0deb4e36..87e75c40822 100644 --- a/doc/customize.dox +++ b/doc/customize.dox @@ -46,41 +46,55 @@ on the output in real time. \subsection minor_tweaks_treeview Navigation -By default Doxygen shows navigation tabs on top of every HTML page, -corresponding with the following settings: +By default, Doxygen will show a title area spanning the full width of the page and +below the contents with a navigation tree as a sidebar on the left hand side of +each HTML page. -- \ref cfg_disable_index "DISABLE_INDEX" = \c NO -- \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c NO - -\image html layout_index_and_notreeview.png -\image latex layout_index_and_notreeview.png width=8cm - -you can switch to an interactive navigation tree as sidebar using +This corresponds to the following settings in the Doxyfile: - \ref cfg_disable_index "DISABLE_INDEX" = \c YES - \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c YES -- \ref cfg_generate_treeview "FULL_SIDEBAR" = \c NO +- \ref cfg_full_sidebar "FULL_SIDEBAR" = \c NO \image html layout_noindex_and_treeview.png \image latex layout_noindex_and_treeview.png width=8cm -you can also make the content span the title area of the screen using +you can make the side bar span the whole height of the page using - \ref cfg_disable_index "DISABLE_INDEX" = \c YES - \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c YES -- \ref cfg_generate_treeview "FULL_SIDEBAR" = \c YES +- \ref cfg_full_sidebar "FULL_SIDEBAR" = \c YES \image html layout_noindex_and_sidebar.png \image latex layout_noindex_and_sidebar.png width=8cm +you can also replace the navigation tree by tabs on top of every HTML page, +corresponding to the following settings: + +- \ref cfg_disable_index "DISABLE_INDEX" = \c NO +- \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c NO + +\image html layout_index_and_notreeview.png +\image latex layout_index_and_notreeview.png width=8cm + or even have both forms of navigation: - \ref cfg_disable_index "DISABLE_INDEX" = \c NO - \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c YES +- \ref cfg_full_sidebar "FULL_SIDEBAR" = \c NO \image html layout_index_and_treeview.png \image latex layout_index_and_treeview.png width=8cm +and this also works with the side bar spanning the full height + +- \ref cfg_disable_index "DISABLE_INDEX" = \c NO +- \ref cfg_generate_treeview "GENERATE_TREEVIEW" = \c YES +- \ref cfg_full_sidebar "FULL_SIDEBAR" = \c YES + +\image html layout_index_and_sidebar.png +\image latex layout_index_and_sidebar.png width=8cm + if you already use an external index (i.e. have one of the following options enabled \ref cfg_generate_htmlhelp "GENERATE_HTMLHELP", @@ -128,11 +142,11 @@ This will create 3 files: contains a couple of commands of the form \$word. These will be replaced by Doxygen on the fly. - footer.html is a HTML fragment which Doxygen normally uses to end - a HTML page. Also here special commands can be used. This file contain the + a HTML page. Also here special commands can be used. This file contains the link to www.doxygen.org and the body and html end tags. - customdoxygen.css is the default cascading style sheet used by Doxygen. It is recommended only to look into this file and overrule - some settings you like by putting them in a separate stylesheets and + some settings you like by putting them in a separate stylesheet and referencing those extra files via \ref cfg_html_extra_stylesheet "HTML_EXTRA_STYLESHEET". @@ -141,8 +155,8 @@ You should edit these files and then reference them from the configuration file. - \ref cfg_html_footer "HTML_FOOTER" = \c footer.html - \ref cfg_html_extra_stylesheet "HTML_EXTRA_STYLESHEET" = \c my_customdoxygen.css -\note it is not longer recommended to use \ref cfg_html_stylesheet "HTML_STYLESHEET", -as it make it difficult to upgrade to a newer version of Doxygen. Use +\note it is no longer recommended to use \ref cfg_html_stylesheet "HTML_STYLESHEET", +as it makes it difficult to upgrade to a newer version of Doxygen. Use \ref cfg_html_extra_stylesheet "HTML_EXTRA_STYLESHEET" instead. See the documentation of the \ref cfg_html_header "HTML_HEADER" tag @@ -154,7 +168,7 @@ it as a source file. Doxygen will copy it for you. \note If you use images or other external content in a custom header you need to make sure these end up in the HTML output directory yourself, -for instance by writing a script that runs Doxygen can then copies the +for instance by writing a script that runs Doxygen and then copies the images to the output. \warning The structure of headers and footers may change after upgrading to @@ -187,7 +201,7 @@ LAYOUT_FILE = DoxygenLayout.xml \endverbatim To change the layout all you need to do is edit the layout file. -The toplevel structure of the file looks as follows: +The top-level structure of the file looks as follows: \verbatim @@ -247,7 +261,7 @@ www.google.com: \endverbatim -The url field can also be a relative URL. If the URL starts with \@ref +The URL field can also be a relative URL. If the URL starts with \@ref the link will point to a documented entities, such as a class, a function, a group, or a related page. Suppose we have defined a page using \@page with label mypage, then a tab with label "My Page" to this page would look @@ -297,6 +311,8 @@ pages generated by Doxygen: documented namespaces (and also Java packages). - The \c concept element represents the layout of all pages generated for documented concepts. +- The \c module element represents the layout of all pages generated for + documented C++ modules. - The \c file element represents the layout of all pages generated for documented files. - The \c group element represents the layout of all pages generated for @@ -316,6 +332,9 @@ The following generic elements are possible for each page:
      Represents the brief description on a page.
      \c detaileddescription
      Represents the detailed description on a page. + +The following generic element is possible for each page except the \c directory page: +
      \c authorsection
      Represents the author section of a page (only used for man pages). This is a separate section for man pages with a text like: @@ -324,7 +343,7 @@ The following generic elements are possible for each page: "\\author" or \ref cmdauthors "\\authors" that generate an author paragraph inside a detailed description.
      -The following generic elements are possible for each page except the concept page: +The following generic element is possible for each page except the \c concept page:
      \c memberdecl
      Represents the quick overview of members on a page (member declarations). @@ -332,13 +351,16 @@ The following generic elements are possible for each page except the concept pag The possible child elements are not listed in detail in the document, but the name of the element should be a good indication of the type of members that the element represents. +
      +The following generic element is possible for each page except the \c concept and \c module page: +
      \c memberdef
      Represents the detailed member list on a page (member definition). Like the \c memberdecl element, also this element has a number of possible child elements.
      -The class page has the following specific elements: +The \c class page has the following specific elements:
      \c includes
      Represents the include file needed to obtain the definition for @@ -357,7 +379,7 @@ The class page has the following specific elements: extracted.
      -The concept page has the following specific elements: +The \c concept page has the following specific elements:
      \c includes
      Represents the include file needed to obtain the definition for @@ -366,7 +388,7 @@ The concept page has the following specific elements:
      Represents the definition of the concept
      -The file page has the following specific elements: +The \c file page has the following specific elements:
      \c includes
      Represents the list of \#include statements contained in this file. @@ -378,16 +400,19 @@ The file page has the following specific elements:
      Represents the link to the source code of this file.
      -The group page has a specific \c groupgraph element which represents the +The \c module page has a specific \c exportedmodules element which represents the +modules that are exported from this module. + +The \c group page has a specific \c groupgraph element which represents the graph showing the dependencies between groups. -Similarly, the directory page has a specific \c directorygraph element +Similarly, the \c directory page has a specific \c directorygraph element which represents the graph showing the dependencies between the directories based on the \#include relations of the files inside the directories. Some elements have a \c visible attribute which can be used to hide the fragment from the generated output, by setting the attribute's -value to "no". You can also use the value of a configuration option to +value to \c no. You can also use the value of a configuration option to determine the visibility, by using its name prefixed with a dollar sign, e.g. \verbatim @@ -405,6 +430,11 @@ this attribute can be used anyway (the default is `visible="yes"`). Some elements have a \c title attribute. This attribute can be used to customize the title Doxygen will use as a header for the piece. +Note that as of doxygen version 1.13.1 and layout version 2.0, Doxygen +will insert defaults for elements that are missing in +the user defined layout file. This allows for the introduction of new elements, without having +to update the user defined layout files to make them appear. +For older Doxygen or layout versions, missing elements are treated as being invisible. \section xmlgenerator Using the XML output diff --git a/doc/diagrams.dox b/doc/diagrams.dox index be82546ab78..817bc2aeaea 100644 --- a/doc/diagrams.dox +++ b/doc/diagrams.dox @@ -24,7 +24,7 @@ cross-platform graph drawing toolkit and can be found at https://www.graphviz.org/ - If you have the "dot" tool in the path, you can set + If you have the "dot" tool in the PATH (environment variable), you can set \ref cfg_have_dot "HAVE_DOT" to \c YES in the configuration file to let Doxygen use it. @@ -82,7 +82,7 @@
    • A \b yellow box indicates a class. A box can have a little marker in the lower right corner to indicate that the class contains base classes that are hidden. - For the class diagrams the maximum tree width is currently 8 elements. + For the class diagrams, the maximum tree width is currently 8 elements. If a tree is wider some nodes will be hidden. If the box is filled with a dashed pattern the inheritance relation is virtual. @@ -122,7 +122,7 @@ include dependency graph) or public inheritance (for the other graphs).
    • A dark green arrow indicates protected inheritance.
    • A dark red arrow indicates private inheritance. -
    • A purple dashed arrow indicated a "usage" relation, the +
    • A purple dashed arrow indicates a "usage" relation, the edge of the arrow is labeled with the variable(s) responsible for the relation. Class \c A uses class \c B, if class \c A has a member variable \c m diff --git a/doc/docblocks.dox b/doc/docblocks.dox index 00c31fc7c07..91b6d289a3e 100644 --- a/doc/docblocks.dox +++ b/doc/docblocks.dox @@ -46,7 +46,7 @@ the concatenation of all comment blocks found within the body of the method or f Having more than one brief or detailed description is allowed (but not recommended, as the order in which the descriptions will appear is not specified). -As the name suggest, a brief description is +As the name suggests, a brief description is a short one-liner, whereas the detailed description provides longer, more detailed documentation. An "in body" description can also act as a detailed description or can describe a collection of implementation details. @@ -167,7 +167,7 @@ Here is an example: in the configuration file, then using Javadoc style comment blocks will automatically start a brief description which ends at the - first dot followed by a space or new line. Here is an example: + first dot, question mark or exclamation mark followed by a space or new line. Here is an example: \verbatim /** Brief description which ends at this dot. Details follow @@ -221,7 +221,7 @@ on the order in which Doxygen parses the code. Unlike most other documentation systems, Doxygen also allows you to put the documentation of members (including global functions) in front of the \e definition. This way the documentation can be placed in the source -file instead of the header file. This keeps the header file compact, and allows the +file instead of the header file. This keeps the header file compact and allows the implementer of the members more direct access to the documentation. As a compromise the brief description could be placed before the declaration and the detailed description before the member definition. @@ -326,7 +326,7 @@ for the Qt style. By default a Javadoc style documentation block behaves the same way as a Qt style documentation block. This is not according the Javadoc specification however, where the first sentence of the documentation block is automatically -treated as a brief description. To enable this behavior you should set +treated as a brief description. To enable this behavior, you should set \ref cfg_javadoc_autobrief "JAVADOC_AUTOBRIEF" to YES in the configuration file. If you enable this option and want to put a dot in the middle of a sentence without ending it, you should put a backslash and a space after it. @@ -353,7 +353,7 @@ block to automatically be treated as a brief description, one may set \subsubsection structuralcommands Documentation at other places -In the examples in the previous section the comment blocks were always located *in +In the examples in the previous section, the comment blocks were always located *in front* of the declaration or definition of a file, class or namespace or *in front* or *after* one of its members. Although this is often comfortable, there may sometimes be reasons to put the @@ -433,8 +433,8 @@ using structural commands: documentation. The disadvantage of this approach is that prototypes are duplicated, so all changes have to be made twice! Because of this you should first consider if this is really needed, and avoid structural - commands if possible. I often receive examples that contain \\fn command - in comment blocks which are place in front of a function. This is clearly + commands if possible. I often receive examples that contain the \\fn command + in comment blocks which are placed in front of a function. This is clearly a case where the \\fn command is redundant and will only lead to problems. When you place a comment block in a file with one of the following extensions @@ -442,7 +442,7 @@ using structural commands: `md` by means of the \ref cfg_extension_mapping "EXTENSION_MAPPING" then Doxygen will hide this file from the file list. - If you have a file that Doxygen cannot parse but still would like to document it, + If you have a file that Doxygen cannot parse but you still would like to document it, you can show it as-is using \ref cmdverbinclude "\\verbinclude", e.g. \verbatim @@ -481,7 +481,7 @@ documentation instead of \"\"\" use \"\"\"! or set \note Instead of \"\"\" one can also use '''. There is also another way to document Python code using comments that -start with "##" or "##<". These type of comment blocks are more in line with the +start with "##" or "##<". These types of comment blocks are more in line with the way documentation blocks work for the other languages supported by Doxygen and this also allows the use of special commands. @@ -497,14 +497,14 @@ Here is the same example again but now using Doxygen style comments: for the corresponding \mbox{\LaTeX} documentation that is generated by Doxygen. \endlatexonly -Since python looks more like Java than like C or C++, you should set +Since Python looks more like Java than like C or C++, you should set \ref cfg_optimize_output_java "OPTIMIZE_OUTPUT_JAVA" to \c YES in the configuration file. \subsection vhdlblocks Comment blocks in VHDL -For VHDL a comment normally start with "--". Doxygen will extract comments +For VHDL a comment normally starts with "--". Doxygen will extract comments starting with "--!". There are only two types of comment blocks in VHDL; a one line "--!" comment representing a brief description, and a multi-line "--!" comment (where the "--!" prefix is repeated for each line) representing @@ -526,7 +526,7 @@ Here is an example VHDL file with Doxygen comments: for the corresponding \mbox{\LaTeX} documentation that is generated by Doxygen. \endlatexonly -As of VHDL 2008 it is also possible to use `/``*` style comments.
      +As of VHDL 2008, it is also possible to use `/``*` style comments.
      Doxygen will handle `/``* ... *``/`as plain comments and `/``*! ... *``/` style comments as special comments to be parsed by Doxygen. @@ -534,7 +534,7 @@ To get proper looking output you need to set \ref cfg_optimize_output_vhdl "OPTIMIZE_OUTPUT_VHDL" to \c YES in the configuration file. This will also affect a number of other settings. When they were not already set correctly Doxygen will produce a warning telling which -settings where overruled. +settings were overruled. \subsection fortranblocks Comment blocks in Fortran @@ -614,7 +614,7 @@ forms of additional markup on top of Markdown formatting. as specified in the C# standard. See \ref xmlcmds for the XML commands supported by Doxygen. -If this is still not enough Doxygen also supports a \ref htmlcmds "subset" of +If this is still not enough, Doxygen also supports a \ref htmlcmds "subset" of the HTML markup language. \htmlonly diff --git a/doc/doxygen.1 b/doc/doxygen.1 index d710cad7cb3..d749027fa8b 100644 --- a/doc/doxygen.1 +++ b/doc/doxygen.1 @@ -3,32 +3,32 @@ Doxygen \- documentation system for various programming languages .SH DESCRIPTION Doxygen is a documentation system for C++, C, Java, Objective-C, IDL -(Corba and Microsoft flavors), Fortran, Python, VHDL and to some extent PHP, C#, and D. +(CORBA and Microsoft flavors), Fortran, Python, VHDL and to some extent PHP, C#, and D. .PP -You can use Doxygen in a number of ways: +You can use Doxygen in several ways: .TP -1) Use Doxygen to generate a template configuration file*: +1) Generate a template configuration file*: .IP doxygen [-s] \fB\-g\fR [configName] .TP -2) Use Doxygen to update an old configuration file*: +2) Update an existing configuration file*: .IP doxygen [-s] \fB\-u\fR [configName] .TP -3) Use Doxygen to generate documentation using an existing configuration file*: +3) Generate documentation using an existing configuration file*: .IP doxygen [configName] .TP -4) Use Doxygen to generate a template file controlling the layout of the generated documentation: +4) Generate a template file that controls the layout of the generated documentation: .IP doxygen \fB\-l\fR [layoutFileName] .IP .RS 0 - In case layoutFileName is omitted DoxygenLayout.xml will be used as filename. - If - is used for layoutFileName Doxygen will write to standard output. + If layoutFileName is omitted, DoxygenLayout.xml will be used as the filename. + If - is used for layoutFileName, Doxygen writes to standard output. .RE .TP -5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex. +5) Generate a template style sheet file for RTF, HTML or LaTeX: .IP RTF: doxygen \fB\-w\fR rtf styleSheetFile @@ -36,9 +36,10 @@ doxygen \fB\-w\fR rtf styleSheetFile HTML: doxygen \fB\-w\fR html headerFile footerFile styleSheetFile [configFile] .IP -LaTeX: doxygen \fB\-w\fR latex headerFile footerFile styleSheetFile [configFile] +LaTeX: +doxygen \fB\-w\fR latex headerFile footerFile styleSheetFile [configFile] .TP -6) Use Doxygen to generate an rtf extensions file +6) Generate an RTF extensions file: .IP RTF: doxygen \fB\-e\fR rtf extensionsFile @@ -47,18 +48,18 @@ doxygen \fB\-e\fR rtf extensionsFile If - is used for extensionsFile Doxygen will write to standard output. .RE .TP -7) Use Doxygen to compare the used configuration file with the template configuration file +7) Compare the used configuration file with the template configuration file: .IP doxygen \fB\-x\fR [configFile] .TP - Use Doxygen to compare the used configuration file with the template configuration file + Compare the used configuration file with the template configuration file .RS 0 - without replacing the environment variables or CMake type replacement variables + without replacing the environment variables or CMake type replacement variables: .RE .IP doxygen \fB\-x_noenv\fR [configFile] .TP -8) Use Doxygen to show a list of built-in emojis. +8) Show a list of built-in emojis. .IP doxygen \fB\-f\fR emoji outputFileName .IP @@ -67,9 +68,9 @@ doxygen \fB\-f\fR emoji outputFileName .RE .PP .RS 0 -*) If \fB\-s\fR is specified the comments of the configuration items in the config file will be omitted. - If configName is omitted 'Doxyfile' will be used as a default. - If - is used for configFile Doxygen will write / read the configuration to /from standard output / input. +*) If \fB\-s\fR is specified, the comments of the configuration items in the config file will be omitted. + If configName is omitted, 'Doxyfile' will be used by default. + If - is used for configFile, Doxygen will write / read the configuration to / from standard output / input. .RE .PP If \fB\-q\fR is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set. diff --git a/doc/doxygen_manual.css b/doc/doxygen_manual.css index 2d225177906..4c9ee06665d 100644 --- a/doc/doxygen_manual.css +++ b/doc/doxygen_manual.css @@ -51,10 +51,6 @@ h1 div.contents { background-color: var(--page-background-color); -} - -div.contents -{ border: 1px solid var(--toc-border-color); border-radius: 7px 7px 7px 7px; } @@ -83,11 +79,20 @@ div.contents, div.headertitle div.headertitle { - font-size: 150%; text-shadow: 0px 2px 2px var(--title-shadow-color); color: var(--title-text-color); } +div.headertitle .title +{ + line-height: 38px; +} + +div.contents { + padding-left: 12px; + padding-right: 12px; +} + @media(min-width: 1120px) { div.contents, div.headertitle @@ -98,3 +103,24 @@ div.contents, div.headertitle } } /* end @media */ + + +body { + scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-background-color); +} + +::-webkit-scrollbar { + background-color: var(--scrollbar-background-color); + height: 12px; + width: 12px; +} +::-webkit-scrollbar-thumb { + border-radius: 6px; + box-shadow: inset 0 0 12px 12px var(--scrollbar-thumb-color); + border: solid 2px transparent; +} +::-webkit-scrollbar-corner { + background-color: var(--scrollbar-background-color); +} + + diff --git a/doc/doxygen_manual.tex b/doc/doxygen_manual.tex index 6ac54c98707..8b500869b8c 100644 --- a/doc/doxygen_manual.tex +++ b/doc/doxygen_manual.tex @@ -16,13 +16,6 @@ \pdfminorversion=7 \pdfsuppresswarningpagegroup=1 \documentclass{book} -%% moved from doxygen.sty due to workaround for LaTex 2019 version and unmaintained tabu package -\usepackage{ifthen} -\ifx\requestedLaTeXdate\undefined -\usepackage{array} -\else -\usepackage{array}[=2016-10-06] -\fi %% \makeatletter % suppress package identification of infwarerr as it contains the word "warning" @@ -41,7 +34,6 @@ \usepackage{geometry} \usepackage{listings} \usepackage{color} -%%\usepackage{ifthen} %% moved to top due to workaround for LaTex 2019 version and unmaintained tabu package \usepackage[table]{xcolor} \PassOptionsToPackage{warn}{textcomp} \usepackage{textcomp} diff --git a/doc/doxygen_manual_chm.css b/doc/doxygen_manual_chm.css index e55758aabdc..1b1c5494508 100644 --- a/doc/doxygen_manual_chm.css +++ b/doc/doxygen_manual_chm.css @@ -155,10 +155,11 @@ dl.el { div.line { font-family: 'JetBrains Mono', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace, fixed; - font-size: 13px; + font-size: 13px; min-height: 13px; line-height: 1.2; - text-wrap: unrestricted; + text-wrap: wrap; + word-break: break-all; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ diff --git a/doc/doxyindexer.1 b/doc/doxyindexer.1 index 2f57a423c7e..eb1fb98ca60 100644 --- a/doc/doxyindexer.1 +++ b/doc/doxyindexer.1 @@ -11,7 +11,7 @@ doxysearch.cgi as a CGI program to search the data indexed by doxyindexer. .SH OPTIONS .TP \fB\-o\fR -The directory where to write the doxysearch.db to. -If omitted the current directory is used. +The directory where doxysearch.db will be written. +If omitted, the current directory is used. .SH SEE ALSO doxygen(1), doxysearch(1), doxywizard(1). diff --git a/doc/doxysearch.1 b/doc/doxysearch.1 index a26107b6330..6626f333bc7 100644 --- a/doc/doxysearch.1 +++ b/doc/doxysearch.1 @@ -1,11 +1,10 @@ .TH DOXYSEARCH "1" "@DATE@" "doxysearch.cgi @VERSION@" "User Commands" .SH NAME -doxysearch.cgi \- search engine used for searching in doxygen documentation. +doxysearch.cgi \- search engine for Doxygen documentation .SH SYNOPSIS .B doxysearch.cgi .SH DESCRIPTION -CGI binary that is used by doxygen generated HTML output to search for words. -The tool uses the search index called \fBdoxysearch.db\fR produced by -doxyindexer. +CGI binary used by Doxygen-generated HTML output to search for words. +The tool uses the search index \fBdoxysearch.db\fR produced by doxyindexer. .SH SEE ALSO doxygen(1), doxyindexer(1), doxywizard(1). diff --git a/doc/doxywizard.1 b/doc/doxywizard.1 index 57eeaab5624..c5c3a6c85ed 100644 --- a/doc/doxywizard.1 +++ b/doc/doxywizard.1 @@ -3,11 +3,9 @@ Doxywizard \- a tool to configure and run Doxygen on your source files .SH SYNOPSIS .B doxywizard +.RI [Doxyfile] .SH DESCRIPTION Doxywizard is an interactive frontend to the Doxygen tool to configure and run Doxygen on your source files. -.PP -You can optionally pass the name of a configuration file as the first argument -to load it at startup. .SH SEE ALSO doxygen(1) diff --git a/doc/doxywizard_usage.dox b/doc/doxywizard_usage.dox index 55efd012510..7bae28a9ce2 100644 --- a/doc/doxywizard_usage.dox +++ b/doc/doxywizard_usage.dox @@ -166,9 +166,11 @@ The representation of the input field depends on the type of the configuration o \image{inline} html expert_string_string.png \image{inline} latex expert_string_string.png width=12.3cm
      Additionally, when the string field should contain a file or a folder name, there are the special buttons - \image{inline} html images/file.png height=14px \ilinebr \image{inline} latex images/file.png height=\DoxyInlineHeightChar + \image{inline} html images/file.svg height=14px + \image{inline} latex images/file.eps height=\DoxyInlineHeightChar and - \image{inline} html images/folder.png height=14px \ilinebr \image{inline} latex images/folder.png height=\DoxyInlineHeightChar + \image{inline} html images/folder.svg height=14px + \image{inline} latex images/folder.eps height=\DoxyInlineHeightChar that start a file / folder selection dialog. A typical field for a file looks like:
      \image{inline} html expert_string_file.png @@ -185,19 +187,24 @@ The representation of the input field depends on the type of the configuration o \image{inline} latex expert_string_image.png width=11.8cm
    • For options taking a list of strings, an editor field is shown with a non-editable list below it. The - \image{inline} html images/add.png height=14px \ilinebr \image{inline} latex images/add.png height=\DoxyInlineHeightChar + \image{inline} html images/add.svg height=14px + \image{inline} latex images/add.eps height=\DoxyInlineHeightChar button adds the string in the editor field to the list and a the - \image{inline} html images/del.png height=14px \ilinebr \image{inline} latex images/del.png height=\DoxyInlineHeightChar + \image{inline} html images/del.svg height=14px + \image{inline} latex images/del.eps height=\DoxyInlineHeightChar button removes the selected string from the list. The - \image{inline} html images/refresh.png height=14px \ilinebr \image{inline} latex images/refresh.png height=\DoxyInlineHeightChar + \image{inline} html images/refresh.svg height=14px + \image{inline} latex images/refresh.eps height=\DoxyInlineHeightChar button can be clicked to replace the selected item in the list with the string entered in the editor field. A typical field looks like:
      \image{inline} html expert_list_string.png \image{inline} latex expert_list_string.png width=11.2cm
      additionally when the list contains file and / or folder names, there are special buttons - \image{inline} html images/file.png height=14px \ilinebr \image{inline} latex images/file.png height=\DoxyInlineHeightChar + \image{inline} html images/file.svg height=14px + \image{inline} latex images/file.eps height=\DoxyInlineHeightChar and - \image{inline} html images/folder.png height=14px \ilinebr \image{inline} latex images/folder.png height=\DoxyInlineHeightChar + \image{inline} html images/folder.svg height=14px + \image{inline} latex images/folder.eps height=\DoxyInlineHeightChar that start a file / folder selection dialog. A typical field would for a file looks like:
      \image{inline} html expert_list_file.png diff --git a/doc/emojisup.dox b/doc/emojisup.dox index f761bd6abe4..421782dc139 100644 --- a/doc/emojisup.dox +++ b/doc/emojisup.dox @@ -16,9 +16,9 @@ */ /*! \page emojisup Emoji support -The [Unicode consortium](http://www.unicode.org/) has defined a set of +The [Unicode Consortium](http://www.unicode.org/) has defined a set of [emoji](https://en.wikipedia.org/wiki/Emoji) with the corresponding Unicode -sequences. Doxygen supports the subset of emoji characters as used by GitHub (based on the list +sequences. Doxygen supports the subset of emoji characters used by GitHub (based on the list https://api.github.com/emojis). An emoji is created using the \ref cmdemoji "\\emoji" command. For example `\emoji smile` or `\emoji :smile:` both produce \emoji smile. @@ -39,7 +39,7 @@ For the different Doxygen output types there is an output defined: \section emojiimage Emoji image retrieval -In the list of images can be downloaded via the following Python script: +In the list, images can be downloaded via the following Python script: \code{.py} # script to download the emoticons from GitHub and to produce a table for # inclusion in Doxygen. Works with python 2.7+ and python 3.x @@ -51,15 +51,15 @@ try: import urllib.request as urlrequest except ImportError: import urllib as urlrequest - + unicode_re = re.compile(r'.*?/unicode/(.*?).png\?.*') - + def get_emojis(): response = urlrequest.urlopen('https://api.github.com/emojis') raw_data = response.read() return json.loads(raw_data) - -def download_images(dir_name): + +def download_images(dir_name, silent): if not os.path.exists(dir_name): os.makedirs(dir_name) json_data = get_emojis() @@ -69,12 +69,21 @@ def download_images(dir_name): image_name = image+'.png' cur_item=cur_item+1 if url.find('/unicode/')==-1 or not os.path.isfile(dir_name+'/'+image_name): + success = True with open(dir_name+'/'+image_name,'wb') as file: - print('%s/%s: fetching %s' % (cur_item,num_items,image_name)) - file.write(urlrequest.urlopen(url).read()) + if not silent: + print('%s/%s: fetching %s' % (cur_item,num_items,image_name)) + try: + file.write(urlrequest.urlopen(url).read()) + except: + print('Unable to fetch %s' % (image_name)) + success = False + if not success: + os.remove(dir_name+'/'+image_name) else: - print('%s/%s: skipping %s' % (cur_item,num_items,image_name)) - + if not silent: + print('%s/%s: skipping %s' % (cur_item,num_items,image_name)) + def produce_table(): json_data = get_emojis() lines = [] @@ -90,20 +99,27 @@ def produce_table(): print("{") print(out_str) print("};") - + if __name__=="__main__": parser = argparse.ArgumentParser() - group = parser.add_mutually_exclusive_group() - group.add_argument('-d','--dir',help='directory to place images in') - group.add_argument('-t','--table',help='generate code fragment',action='http://github.com/store_true') + + parser.add_argument('-d','--dir',help='directory to place images in') + parser.add_argument('-t','--table',help='generate code fragment',action='http://github.com/store_true') + parser.add_argument('-s','--silent',help='silent mode',action='http://github.com/store_true') args = parser.parse_args() + if args.table: produce_table() - else: - download_images(args.dir) - + if args.dir: + download_images(args.dir, args.silent) \endcode -When invoking it with the `-d image_dir` option the images will by downloaded in the `image_dir` directory. + +When invoking the script with the `-d image_dir` option, +the images will be downloaded to the `image_dir` directory. + +When invoking the script with the `-s` option, +no progress messages are shown while fetching the images, except when fetching an image fails. + By means of the Doxygen configuration parameter \ref cfg_latex_emoji_directory "LATEX_EMOJI_DIRECTORY" the requested directory can be selected. diff --git a/doc/faq.dox b/doc/faq.dox index 2619239aa97..16d9f69a4b3 100644 --- a/doc/faq.dox +++ b/doc/faq.dox @@ -136,7 +136,7 @@ in the documentation of the class MyClassName regardless of the name of the actu header file in which the definition of MyClassName is contained. If you want Doxygen to show that the include file should be included using -quotes instead of angle brackets you should type: +quotes instead of angle brackets, you should type: \verbatim /*! \class MyClassName myhdr.h "path/myhdr.h" * @@ -308,7 +308,7 @@ so I started to write my own tool... When redirecting all the console output of Doxygen, i.e. messages and warnings, this can be interleaved or in a non-expected order. -The, technical, reason for this is that the `stdout` can be buffered. +The technical reason for this is that the `stdout` can be buffered. It is possible to overcome this by means of the `-b` of Doxygen, like e.g `doxygen -b > out.txt 2>&1`. Note this might cost a little more time though. diff --git a/doc/htmlcmds.dox b/doc/htmlcmds.dox index 49761bbbe3a..6d76a96d56f 100644 --- a/doc/htmlcmds.dox +++ b/doc/htmlcmds.dox @@ -32,14 +32,14 @@ of a HTML tag are passed on to the HTML output only (if supported by the output format). \startalign\anchor htmltag_A_NAME \addindex "\"\endalign\Starts a named anchor (if supported by the output format). -\startalign\anchor htmltag_endA \addindex "\"\endalign\Ends a link or anchor +\endhtmltag{A}Ends a link or anchor \startendhtmltag{B}Starts and ends a piece of text displayed in a bold font. \startendhtmltag{BLOCKQUOTE}Starts and ends a quotation block. -\startalign\anchor htmltag_BR \addindex "\"\endalign\Forces a line break. +\starthtmltag{BR}Forces a line break. \startendhtmltag{CENTER}Starts and ends a section of centered text. \startendhtmltag{CAPTION}Starts and ends a caption. Use within a table only. \startendhtmltag{CITE}Starts and ends a section of text displayed in a font specific for citations. -\startendhtmltag{CODE}Starts and ends a piece of text displayed in a typewriter font. +\startendhtmltag{CODE}Starts and ends a piece of text displayed in a typewriter font. Note that only for C# code, this command is equivalent to \ref cmdcode "\\code" (see \ref xmltag_code "\"). \startendhtmltag{DD}Starts and ends an item description. @@ -50,10 +50,10 @@ of a HTML tag are passed on to the HTML output only \startendhtmltag{DL}Starts and ends a description list. \startendhtmltag{DT}Starts and ends an item title. \startendhtmltag{EM}Starts and ends a piece of text displayed in an italic font. -\startalign\anchor htmltag_HR \addindex "\"\endalign\Writes a horizontal ruler. +\starthtmltag{HR}Writes a horizontal ruler. \startendhtmltag{H1}Starts and ends an unnumbered section. \startendhtmltag{H2}Starts and ends an unnumbered subsection. -\startendhtmltag{H3}Starts and ends an unnumbered subsubsection. +\startendhtmltag{H3}Starts and ends an unnumbered subsubsection. \startendhtmltag{H4}Starts and ends an unnumbered subsubsection. \startendhtmltag{H5}Starts and ends an unnumbered subsubsection. \startendhtmltag{H6}Starts and ends an unnumbered subsubsection. @@ -86,7 +86,7 @@ of a HTML tag are passed on to the HTML output only Finally we have the HTML style comments. -- When using the, Doxygen, special HTML style comments, i.e. \ @@ -368,7 +368,7 @@ The list of special HTML4 character entities with their descriptions has been ta | \‹ | ‹ | single left-pointing angle quotation mark | | \› | › | single right-pointing angle quotation mark | | \€ | € | euro sign | -| **Doxygen extensions:** ||| +| **Doxygen extensions** ||| | \&tm; | &tm; | trade mark sign | | \' | ' | apostrophe| diff --git a/doc/index.dox b/doc/index.dox index 15a503d422f..3e6d7a92c25 100644 --- a/doc/index.dox +++ b/doc/index.dox @@ -34,8 +34,8 @@ Doxygen also supports the hardware description language VHDL. Doxygen can help you in three ways:
        -
      1. It can generate an on-line documentation browser (in HTML) and/or an - off-line reference manual (in \LaTeX) from a set +
      2. It can generate an online documentation browser (in HTML) and/or an + offline reference manual (in \LaTeX) from a set of documented source files. There is also support for generating output in RTF (MS-Word), PostScript, hyperlinked PDF, compressed HTML, and Unix man pages. @@ -49,7 +49,7 @@ Doxygen can help you in three ways: by means of include dependency graphs, inheritance diagrams, and collaboration diagrams, which are all generated automatically.
      3. You can also use Doxygen for creating normal documentation (as I did - for the Doxygen user manual and web-site). + for the Doxygen user manual and website).
      Doxygen is developed under macOS and Linux, but is set-up to be highly @@ -68,7 +68,7 @@ The first part forms a user manual: documentation quickly.
    • Section \ref docblocks demonstrates the various ways that code can be documented. -
    • Section \ref markdown show the Markdown formatting supported by Doxygen. +
    • Section \ref markdown shows the Markdown formatting supported by Doxygen.
    • Section \ref lists shows how to create lists.
    • Section \ref grouping shows how to group things together.
    • Section \ref tables shows how to insert tables in the documentation. @@ -81,10 +81,10 @@ The first part forms a user manual: supported by Doxygen.
    • Section \ref searching shows various ways to search in the HTML documentation.
    • Section \ref extsearch shows how use the external search and index tools -
    • Section \ref additional explains how you can create non-api related documentation pages. +
    • Section \ref additional explains how you can create non-API related documentation pages.
    • Section \ref customize explains how you can customize the output generated by Doxygen. -
    • Section \ref custcmd show how to define and use custom commands in your comments. +
    • Section \ref custcmd shows how to define and use custom commands in your comments.
    • Section \ref external explains how to let Doxygen create links to externally generated documentation.
    • Section \ref faq gives answers to frequently asked questions.
    • Section \ref trouble tells you what to do when you have problems. @@ -104,7 +104,7 @@ The second part forms a reference manual: can be used within the documentation.
    • Section \ref xmlcmds shows an overview of the C# style XML commands that can be used within the documentation. -
    • Section \ref emojisup shows an introduction how emoji can be used within +
    • Section \ref emojisup shows an introduction to how emoji can be used within the documentation.
    @@ -150,7 +150,7 @@ If you know other projects, let download page -to get the latest distribution, if you have not downloaded Doxygen already. +to get the latest distribution if you have not downloaded Doxygen already. \section install_src_unix Compiling from source on UNIX @@ -37,7 +37,7 @@ following to build the executable: \addindex python
  • You need \c python (version 2.7 or higher, see https://www.python.org).
  • In order to generate a \c Makefile for your platform, you need - cmake version 3.14 or later. + Cmake version 3.14 or later. \addindex cmake @@ -46,7 +46,7 @@ tools should be installed.
    • Qt Software's GUI toolkit - Qt + Qt \addindex Qt version 5.14 or higher (including Qt 6). This is needed to build the GUI front-end Doxywizard. @@ -66,7 +66,13 @@ tools should be installed. www.ghostscript.com.
    -Compilation is now done by performing the following steps: +For testing at least these additional dependencies should be available: +
      +
    • xmllint (libxml2-utils)
    • +
    • bibtex (texlive-binaries)
    • +
    + +Compilation steps:
    1. Unpack the archive, unless you already have done that: @@ -85,7 +91,7 @@ Compilation is now done by performing the following steps: cmake -G "Unix Makefiles" .. cmake tries to determine the platform you use, and will look - for the requires tools. It will report if something is missing. + for the required tools. It will report if something is missing. If you have Qt-5.14 or higher installed and want to build the GUI front-end, you should enable it as follows: @@ -175,7 +181,8 @@ or MinGW. \addindex flex \addindex bison The next step is to install modern versions of \c bison and \c flex -(see https://sourceforge.net/projects/winflexbison/. After installation and adding them to +(see https://sourceforge.net/projects/winflexbison/files and select win_flex_bison3-latest.zip. +After installation and adding them to your `path` rename `win_flex.exe` to `flex.exe` and `win_bison.exe` to `bison.exe`) \addindex python Furthermore you have to install \c python (version 2.7 or higher, see https://www.python.org). @@ -183,7 +190,7 @@ These packages are needed during the compilation process. Download Doxygen's source tarball and put it somewhere (e.g. use c:\\tools) -Now start a visual studio native command shell (for either x86 or x64) and type +Now start a Visual Studio native command shell (for either x86 or x64) and type \verbatim cd c:\tools tar zxvf doxygen-x.y.z.src.tar.gz @@ -200,7 +207,7 @@ mkdir build cd build cmake -G "Visual Studio 14 2015" .. \endverbatim -This will create a project file then can be opened in Visual Studio. +This will create a project file that can be opened in Visual Studio. If you prefer compiling from the command prompt you can use the following instead: \verbatim @@ -229,7 +236,7 @@ of the GraphViz package to render nicer diagrams, see the If you want to produce compressed HTML files (see \ref cfg_generate_htmlhelp "GENERATE_HTMLHELP") in the configuration file, then you need the Microsoft HTML help workshop. -In the beginning of 2021 Microsoft took the original page, with a.o. the download links, +At the beginning of 2021, Microsoft took the original page, with a.o. the download links, offline the HTML help workshop was already many years in maintenance mode). You can download the HTML help workshop from the web archives at Installation executable. diff --git a/doc/layout_index_and_sidebar.png b/doc/layout_index_and_sidebar.png new file mode 100644 index 00000000000..1380bbed7db Binary files /dev/null and b/doc/layout_index_and_sidebar.png differ diff --git a/doc/maintainers.txt b/doc/maintainers.txt index 83922b48ee3..436d291ec36 100644 --- a/doc/maintainers.txt +++ b/doc/maintainers.txt @@ -91,7 +91,7 @@ Alessandro Falappa: alex dot falappa at gmail dot com Ahmed Aldo Faisal: aaf23 at cam dot ac dot uk TranslatorJapanese -Suzumizaki-Kimikata: szmml at h12u.com +Suzumizaki-Kimikata: szmml at h12u dot com Hiroki Iseri: goyoki at gmail dot com Ryunosuke Satoh: sun594 at hotmail dot com Kenji Nagamatsu: [unreachable] naga at joyful dot club dot ne dot jp @@ -103,7 +103,8 @@ SooYoung Jung: jung5000 at gmail dot com Richard Kim: [unreachable] ryk at dspwiz dot com TranslatorLatvian -Lauris: lauris at nix.lv +Lauris: lauris at nix dot lv +Fedosov Artyom Dmitrievich: artjomsfedosovs2 at gmail dot com TranslatorLithuanian Tomas Simonaitis: [unreachable] haden at homelan dot lt @@ -158,7 +159,7 @@ Francisco Oltra Thennet: [unreachable] foltra at puc dot cl David Vaquero: david at grupoikusnet dot com TranslatorSwedish -Björn Palmqvist: bjorn.palmqvist at aidium.se +Björn Palmqvist: bjorn dot palmqvist at aidium dot se TranslatorTurkish Emin Ilker Cetinbas: niw3 at yahoo dot com diff --git a/doc/markdown.dox b/doc/markdown.dox index 2752b2df609..84b3cc258e5 100644 --- a/doc/markdown.dox +++ b/doc/markdown.dox @@ -162,6 +162,7 @@ seen as a level 2 header (see \ref md_headers). To emphasize a text fragment you start and end the fragment with an underscore or star. Using two stars or underscores will produce strong emphasis. +Three stars or underscores will combine the emphasis from the previous two options. Examples: @@ -172,6 +173,10 @@ Examples: * **double asterisks** * * __double underscores__ +* +* ***triple asterisks*** +* +* ___triple underscores___ See section \ref mddox_emph_spans for more info how Doxygen handles emphasis / strikethrough spans slightly different than standard / Markdown GitHub Flavored Markdown. @@ -301,6 +306,8 @@ Also here you can use \@ref to link to an image: The caption text is optional. +\note Don't forget to add the path of the image to the \ref cfg_image_path "IMAGE_PATH". + \subsection md_autolink Automatic Linking To create a link to an URL or e-mail address Markdown supports the following @@ -451,7 +458,8 @@ which will produce: int func(int a,int b) { return a*b; } ~~~~~~~~~~~~~~~ -The curly braces and dot are optional by the way. +The dot is optional, the curly braces are optional when the that language name begins with an +alphabetical character and further characters are alphanumerical characters or a plus sign. Another way to denote fenced code blocks is to use 3 or more backticks (```): @@ -537,7 +545,12 @@ be `.md` or `.markdown` (see \ref cfg_extension_mapping "EXTENSION_MAPPING" if your Markdown files have a different extension, and use `md` as the name of the parser). Each file is converted to a page (see the \ref cmdpage "page" command for -details). +details). Doxygen will not create a dedicated page if the Markdown file +starts with a dedicated command (a.o. `\defgroup`, `\dir`) to avoid creating +an empty page when the file only contains directory or group documentation. +A `README.md` file in a subdirectory will be treated as directory +documentation, unless it is explicitly overruled by a dedicated command +(a.o. `@page`, `@mainpage`) to create a new page. By default the name and title of the page are derived from the file name. If the file starts with a level 1 header however, it is used as the title @@ -642,7 +655,7 @@ stars or tildes, so the following will appear as-is: Furthermore, a `*` or `_` only starts an emphasis and a `~` only starts a strikethrough if - it is followed by an alphanumerical character, and -- it is preceded by a space, newline, or one the following characters `<{([,:;` +- it is preceded by a space, newline, or one of the following characters `<{([,:;` An emphasis or a strikethrough ends if - it is not followed by an alphanumerical character, and @@ -680,6 +693,10 @@ added for backward compatibility reasons. In case you want to have single quotes inside a code span, don't use one backtick but two backticks around the code span. +Double backticks are also ended by double quotes in the same way. + + A ``cool'' word in a ``nice'' sentence. + \subsection mddox_lists Lists Extensions With Markdown two lists separated by an empty line are joined together into diff --git a/doc/translator.py b/doc/translator.py index 6e40f7c8d1b..68918af9dd1 100755 --- a/doc/translator.py +++ b/doc/translator.py @@ -62,7 +62,7 @@ was prefixed by backslash (was LaTeX related error). 2013/02/19 - Better diagnostics when translator_xx.h is too crippled. 2013/06/25 - TranslatorDecoder checks removed after removing the class. - 2013/09/04 - Coloured status in langhowto. *ALMOST up-to-date* category + 2013/09/04 - Colored status in langhowto. *ALMOST up-to-date* category of translators introduced. 2014/06/16 - unified for Python 2.6+ and 3.0+ """ @@ -70,7 +70,6 @@ from __future__ import print_function import os -import platform import re import sys import textwrap @@ -487,7 +486,7 @@ def __collectClassInfo(self, tokenIterator): self.status = '0.0.00' # Check whether status was set, or set 'strange'. - if self.status == None: + if self.status is None: self.status = 'strange' if not self.readableStatus: self.readableStatus = 'strange' @@ -765,7 +764,7 @@ def __collectPublicMethodPrototypes(self, tokenIterator): the source file.""" assert(self.classId != 'Translator') - assert self.baseClassId != None, 'Class ' + self.classId + ' from the file ' + self.fname + ' does not have a base class.' + assert self.baseClassId is not None, 'Class ' + self.classId + ' from the file ' + self.fname + ' does not have a base class.' # The following finite automaton slightly differs from the one # inside self.collectPureVirtualPrototypes(). It produces the @@ -782,7 +781,6 @@ def __collectPublicMethodPrototypes(self, tokenIterator): # identifiers. prototype = '' # readable prototype (with everything) uniPrototype = '' # unified prototype (without arg. identifiers) - warning = '' # warning message -- if something special detected methodId = None # processed method id # Collect the method prototypes. Stop on the closing @@ -1097,7 +1095,7 @@ def processing(self): # Eat the rest of the source to cause closing the file. while True: try: - t = next(tokenIterator) + next(tokenIterator) except StopIteration: break @@ -1414,7 +1412,8 @@ def __extractProcessedInfo(self): # of the list. langReadableLst = [] for name, obj in self.langLst: - if obj.status == 'En': continue + if obj.status == 'En': + continue # Append the 'En' to the classId to possibly obtain the classId # of the English-based object. If the object exists, modify the @@ -1665,7 +1664,8 @@ def generateTranslatorReport(self): f.write(' %-6s' % obj.readableStatus) numimpl = len(obj.missingMethods) pluralS = '' - if numimpl > 1: pluralS = 's' + if numimpl > 1: + pluralS = 's' percent = 100 * numimpl / numRequired f.write('\t%2d method%s to implement (%d %%)' % ( numimpl, pluralS, percent)) @@ -1693,7 +1693,8 @@ def generateTranslatorReport(self): lst.sort() plural = len(lst) > 1 note = 'Note: The adapter class' - if plural: note += 'es' + if plural: + note += 'es' note += ' ' + ', '.join(lst) if not plural: note += ' is' @@ -1777,7 +1778,6 @@ def __loadMaintainers(self): inside = False # inside the record for the language lineReady = True classId = None - maintainersLst = None self.__maintainersDic = {} while lineReady: line = f.readline() # next line @@ -1787,14 +1787,13 @@ def __loadMaintainers(self): if line != '' and line[0] == '%': # skip the comment line continue - if not inside: # if outside of the record + if not inside: # if outside of the record if line != '': # should be language identifier classId = line - maintainersLst = [] inside = True # Otherwise skip empty line that do not act as separator. - else: # if inside the record + else: # if inside the record if line == '': # separator found inside = False else: @@ -1871,28 +1870,23 @@ def generateLanguageDoc(self): # Define templates for HTML table parts of the documentation. htmlTableTpl = ''' - \\htmlonly -

      - - - -
      - - - - - - + \\latexonly + \\footnotesize + \\endlatexonly +
      Language Maintainer Contact address - (replace the at and dot) Status
      + + + + + %s -
      LanguageMaintainerContact address (replace the at and dot)Status
      -
      -

      - \\endhtmlonly + \\latexonly + \\normalsize + \\endlatexonly ''' htmlTableTpl = textwrap.dedent(htmlTableTpl) htmlTrTpl = '\n %s\n ' @@ -1964,7 +1958,7 @@ def generateLanguageDoc(self): # The last element contains the readable form of the status. bgcolor = self.getBgcolorByReadableStatus(obj.readableStatus) - lst.append(htmlTdStatusColorTpl % (bgcolor, obj.readableStatus)) + lst.append(htmlTdStatusColorTpl % (bgcolor, obj.readableStatus.replace(".","\\."))) # Join the table data to one table row. trlst.append(htmlTrTpl % (''.join(lst))) @@ -1972,77 +1966,8 @@ def generateLanguageDoc(self): # Join the table rows and insert into the template. htmlTable = htmlTableTpl % (''.join(trlst)) - # Define templates for LaTeX table parts of the documentation. - latexTableTpl = r''' - \latexonly - \footnotesize - \begin{longtable}{|l|l|l|l|} - \hline - {\bf Language} & {\bf Maintainer} & {\bf Contact address} & {\bf Status} \\ - \hline - %s - \hline - \end{longtable} - \normalsize - \endlatexonly - ''' - latexTableTpl = textwrap.dedent(latexTableTpl) - latexLineTpl = '\n' + r' %s & %s & {\tt\tiny %s} & %s \\' - - # Loop through transl objects in the order of sorted readable names - # and add generate the content of the LaTeX table. - trlst = [] - for name, obj in self.langLst: - # For LaTeX, more maintainers for the same language are - # placed on separate rows in the table. The line separator - # in the table is placed explicitly above the first - # maintainer. Prepare the arguments for the LaTeX row template. - maintainers = [] - if obj.classId in self.__maintainersDic: - maintainers = self.__maintainersDic[obj.classId] - - lang = obj.langReadable - maintainer = None # init - email = None # init - if obj.status == 'En': - # Check whether there is the coupled non-English. - classId = obj.classId[:-2] - if classId in self.__translDic: - langNE = self.__translDic[classId].langReadable - maintainer = 'see the %s language' % langNE - email = '~' - - if not maintainer and (obj.classId in self.__maintainersDic): - lm = [ m[0] for m in self.__maintainersDic[obj.classId] ] - maintainer = maintainers[0][0] - email = maintainers[0][1] - - status = obj.readableStatus - - # Use the template to produce the line of the table and insert - # the hline plus the constructed line into the table content. - # The underscore character must be escaped. - trlst.append('\n \\hline') - s = latexLineTpl % (lang, maintainer, email, status) - s = s.replace('_', '\\_') - trlst.append(s) - - # List the other maintainers for the language. Do not set - # lang and status for them. - lang = '~' - status = '~' - for m in maintainers[1:]: - maintainer = m[0] - email = m[1] - s = latexLineTpl % (lang, maintainer, email, status) - s = s.replace('_', '\\_') - trlst.append(s) - - # Join the table lines and insert into the template. - latexTable = latexTableTpl % (''.join(trlst)) - # Put the HTML and LaTeX parts together and define the dic item. - tplDic['informationTable'] = htmlTable + '\n' + latexTable + tplDic['informationTable'] = htmlTable # Insert the symbols into the document template and write it down. f = xopen(fDocName, 'w') diff --git a/doc_internal/CMakeLists.txt b/doc_internal/CMakeLists.txt index 06cbdef8636..178c8e7c017 100644 --- a/doc_internal/CMakeLists.txt +++ b/doc_internal/CMakeLists.txt @@ -19,7 +19,7 @@ else() find_package(Doxygen) endif() -configure_file(${CMAKE_SOURCE_DIR}/doc_internal/Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY) set(DOC_FILES language.tpl @@ -27,7 +27,7 @@ set(DOC_FILES translator.py ) -file(GLOB LANG_FILES CONFIGURE_DEPENDS "${TOP}/src//translator_??.h") +file(GLOB LANG_FILES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src//translator_??.h") foreach (f ${DOC_FILES}) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/doc_internal/${f} diff --git a/doc_internal/Doxyfile.in b/doc_internal/Doxyfile.in index 71d3d2e64eb..554ff2b9759 100644 --- a/doc_internal/Doxyfile.in +++ b/doc_internal/Doxyfile.in @@ -8,7 +8,7 @@ PROJECT_NAME = Doxygen PROJECT_NUMBER = PROJECT_BRIEF = PROJECT_LOGO = -PROJECT_ICON = @CMAKE_SOURCE_DIR@/templates/icon/doxygen.ico +PROJECT_ICON = @CMAKE_CURRENT_SOURCE_DIR@/../templates/icon/doxygen.ico OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doxygen_docs CREATE_SUBDIRS = YES CREATE_SUBDIRS_LEVEL = 8 @@ -20,8 +20,8 @@ ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ -STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@ +STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@/.. +STRIP_FROM_INC_PATH = @CMAKE_CURRENT_SOURCE_DIR@/.. SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES JAVADOC_BANNER = NO @@ -115,15 +115,15 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = @CMAKE_SOURCE_DIR@/doc_internal/doxygen.md \ +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/doxygen.md \ @PROJECT_BINARY_DIR@/doc_internal/commands_history.md \ - @CMAKE_SOURCE_DIR@/doc_internal/commands_internal.md \ - @CMAKE_SOURCE_DIR@/doc_internal/releases.md \ + @CMAKE_CURRENT_SOURCE_DIR@/commands_internal.md \ + @CMAKE_CURRENT_SOURCE_DIR@/releases.md \ @PROJECT_BINARY_DIR@/doc_internal/tags_history.md \ @PROJECT_BINARY_DIR@/doc_internal/translator_report.md \ - @CMAKE_SOURCE_DIR@/src \ - @CMAKE_SOURCE_DIR@/vhdlparser \ - @CMAKE_SOURCE_DIR@/libxml + @CMAKE_CURRENT_SOURCE_DIR@/../src \ + @CMAKE_CURRENT_SOURCE_DIR@/../vhdlparser \ + @CMAKE_CURRENT_SOURCE_DIR@/../libxml INPUT_ENCODING = UTF-8 INPUT_FILE_ENCODING = FILE_PATTERNS = *.h \ @@ -172,7 +172,7 @@ HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = -HTML_EXTRA_STYLESHEET = @CMAKE_SOURCE_DIR@/doc_internal/doc_internal.css +HTML_EXTRA_STYLESHEET = @CMAKE_CURRENT_SOURCE_DIR@/doc_internal.css HTML_EXTRA_FILES = HTML_COLORSTYLE = TOGGLE HTML_COLORSTYLE_HUE = 220 @@ -305,9 +305,9 @@ ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES -INCLUDE_PATH = @CMAKE_SOURCE_DIR@/deps/libmd5 \ - @CMAKE_SOURCE_DIR@/deps/liblodepng \ - @CMAKE_SOURCE_DIR@/deps/libmscgen +INCLUDE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/../deps/libmd5 \ + @CMAKE_CURRENT_SOURCE_DIR@/../deps/liblodepng \ + @CMAKE_CURRENT_SOURCE_DIR@/../deps/libmscgen INCLUDE_FILE_PATTERNS = PREDEFINED = DOXYGEN_ONLY EXPAND_AS_DEFINED = DOC_NODES \ @@ -356,6 +356,7 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = PLANTUML_CFG_FILE = PLANTUML_INCLUDE_PATH = +PLANTUMLFILE_DIRS = DOT_GRAPH_MAX_NODES = 300 MAX_DOT_GRAPH_DEPTH = 0 DOT_MULTI_TARGETS = YES diff --git a/doc_internal/cmds_tags.py b/doc_internal/cmds_tags.py index 6dee796e9ef..7dd7445b901 100644 --- a/doc_internal/cmds_tags.py +++ b/doc_internal/cmds_tags.py @@ -1,7 +1,9 @@ #!/usr/bin/env python from __future__ import print_function -import argparse, glob, itertools, re, shutil, os, sys +import re +import os +import sys import subprocess import shlex @@ -70,7 +72,7 @@ def get_commands(version): if cmds_reg.match(line): lst_list.append(re.sub(cmds_reg,'',line).replace("\\\\","\\")) elif line.startswith("\\endsecreflist"): - break; + break return lst_list @@ -80,13 +82,13 @@ def process_commands(old_version,new_version): old_list = get_commands(old_version) new_list = get_commands(new_version) hits = [] - for l in new_list: + for n in new_list: toadd = True for o in old_list: - if l == o: + if n == o: toadd = False if toadd: - hits.append(l) + hits.append(n) cmds = "" first = True @@ -121,21 +123,21 @@ def process_tags(old_version,new_version): (new_active,new_obsolete) = get_tags(new_version) hits_active = [] hits_obsolete = [] - for l in new_active: + for n in new_active: toadd = True for o in old_active: - if l == o: + if n == o: toadd = False if toadd: - hits_active.append(l) + hits_active.append(n) - for l in new_obsolete: + for n in new_obsolete: toadd = True for o in old_obsolete: - if l == o: + if n == o: toadd = False if toadd: - hits_obsolete.append(l) + hits_obsolete.append(n) tags= "" if len(hits_active) != 0: diff --git a/doc_internal/commands_internal.md b/doc_internal/commands_internal.md index 5f41fdb415d..1ec9e2e1b27 100755 --- a/doc_internal/commands_internal.md +++ b/doc_internal/commands_internal.md @@ -7,6 +7,7 @@ and the version in which they were introduced. \refitem cmdialias \\ialias \refitem cmdendicode \\endicode \refitem cmdendiliteral \\endiliteral +\refitem cmdendiskip \\endiskip \refitem cmdendiverbatim \\endiverbatim \refitem cmdianchor \\ianchor \refitem cmdicode \\icode @@ -16,6 +17,7 @@ and the version in which they were introduced. \refitem cmdiliteral \\iliteral \refitem cmdiprefix \\iprefix \refitem cmdiraise \\iraise +\refitem cmdiskip \\iskip \refitem cmdiverbatim \\iverbatim \endsecreflist @@ -140,6 +142,17 @@ and the version in which they were introduced. \since doxygen version 1.11.0 +


      +\section cmdiskip \\iskip + \addindex \\iskip + + Internal doxygen command to suppress evaluation of whitespace to determine the indentation + of a comment block. Evaluation will continue as normal when the matching + \ref cmdendiskip "\\endiskip" command is found. + Inserted when processing commands that contain literal text like `\startuml`, `\verbatim`, `` etc. + +\since doxygen version 1.12.0 +
      \section cmdiprefix \\iprefix "
    tag without matching
      "); } else { - retval=RetVal_EndList; + retval = Token::make_RetVal_EndList(); } break; - case HTML_LI: + case HtmlTagType::HTML_LI: if (!insideLI(thisVariant())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found tag without matching
    1. "); @@ -5111,181 +5484,187 @@ int DocPara::handleHtmlEndTag(const QCString &tagName) // ignore
    2. tags } break; - case HTML_DETAILS: + case HtmlTagType::HTML_DETAILS: if (!insideDetails(thisVariant())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found tag without matching
      "); } else { - retval=RetVal_EndHtmlDetails; + retval = Token::make_RetVal_EndHtmlDetails(); } break; - case HTML_BLOCKQUOTE: + case HtmlTagType::HTML_BLOCKQUOTE: if (!insideBlockQuote(thisVariant())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found tag without matching
      "); } else { - retval=RetVal_EndBlockQuote; + retval = Token::make_RetVal_EndBlockQuote(); } break; - case HTML_BOLD: + case HtmlTagType::HTML_BOLD: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Bold,tagName); break; - case HTML_S: + case HtmlTagType::HTML_S: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::S,"s"); break; - case HTML_STRIKE: + case HtmlTagType::HTML_STRIKE: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Strike,tagName); break; - case HTML_DEL: + case HtmlTagType::HTML_DEL: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Del,tagName); break; - case HTML_UNDERLINE: + case HtmlTagType::HTML_UNDERLINE: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Underline,tagName); break; - case HTML_INS: + case HtmlTagType::HTML_INS: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Ins,tagName); break; - case HTML_CODE: + case HtmlTagType::HTML_CODE: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Code,tagName); break; - case HTML_EMPHASIS: + case HtmlTagType::HTML_KBD: + parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Kbd,tagName); + break; + case HtmlTagType::HTML_TT: + parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Typewriter,tagName); + break; + case HtmlTagType::HTML_EMPHASIS: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Italic,tagName); break; - case HTML_DIV: + case HtmlTagType::HTML_DIV: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName); break; - case HTML_SPAN: + case HtmlTagType::HTML_SPAN: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName); break; - case HTML_SUB: + case HtmlTagType::HTML_SUB: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Subscript,tagName); break; - case HTML_SUP: + case HtmlTagType::HTML_SUP: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Superscript,tagName); break; - case HTML_CENTER: + case HtmlTagType::HTML_CENTER: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Center,tagName); break; - case HTML_SMALL: + case HtmlTagType::HTML_SMALL: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Small,tagName); break; - case HTML_CITE: + case HtmlTagType::HTML_CITE: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Cite,tagName); break; - case HTML_PRE: + case HtmlTagType::HTML_PRE: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Preformatted,tagName); setInsidePreformatted(FALSE); parser()->tokenizer.setInsidePre(FALSE); break; - case HTML_P: - retval=TK_NEWPARA; + case HtmlTagType::HTML_P: + retval = Token::make_TK_NEWPARA(); break; - case HTML_DL: - retval=RetVal_EndDesc; + case HtmlTagType::HTML_DL: + retval = Token::make_RetVal_EndDesc(); break; - case HTML_DT: + case HtmlTagType::HTML_DT: // ignore tag break; - case HTML_DD: + case HtmlTagType::HTML_DD: // ignore tag break; - case HTML_TABLE: - retval=RetVal_EndTable; + case HtmlTagType::HTML_TABLE: + retval = Token::make_RetVal_EndTable(); break; - case HTML_TR: - // ignore tag + case HtmlTagType::HTML_TR: + retval = Token::make_RetVal_EndTableRow(); break; - case HTML_TD: - // ignore tag + case HtmlTagType::HTML_TD: + retval = Token::make_RetVal_EndTableCell(); break; - case HTML_TH: - // ignore tag + case HtmlTagType::HTML_TH: + retval = Token::make_RetVal_EndTableCell(); break; - case HTML_THEAD: - case HTML_TBODY: - case HTML_TFOOT: + case HtmlTagType::HTML_THEAD: + case HtmlTagType::HTML_TBODY: + case HtmlTagType::HTML_TFOOT: // for time being ignore tag break; - case HTML_CAPTION: + case HtmlTagType::HTML_CAPTION: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_BR: + case HtmlTagType::HTML_BR: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal
      tag found"); break; - case HTML_H1: + case HtmlTagType::HTML_H1: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_H2: + case HtmlTagType::HTML_H2: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_H3: + case HtmlTagType::HTML_H3: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_H4: + case HtmlTagType::HTML_H4: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_H5: + case HtmlTagType::HTML_H5: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_H6: + case HtmlTagType::HTML_H6: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag found"); break; - case HTML_IMG: + case HtmlTagType::HTML_IMG: break; - case HTML_HR: + case HtmlTagType::HTML_HR: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal tag found"); break; - case HTML_A: + case HtmlTagType::HTML_A: //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag
      found"); // ignore tag (can be part of break; - case XML_TERM: + case HtmlTagType::XML_TERM: break; - case XML_SUMMARY: - retval=TK_NEWPARA; + case HtmlTagType::XML_SUMMARY: + retval = Token::make_TK_NEWPARA(); break; - case XML_REMARKS: - case XML_PARA: - case XML_VALUE: - case XML_EXAMPLE: - case XML_PARAM: - case XML_LIST: - case XML_TYPEPARAM: - case XML_RETURNS: - case XML_SEE: - case XML_SEEALSO: - case XML_EXCEPTION: - case XML_INHERITDOC: - retval = RetVal_CloseXml; + case HtmlTagType::XML_REMARKS: + case HtmlTagType::XML_PARA: + case HtmlTagType::XML_VALUE: + case HtmlTagType::XML_EXAMPLE: + case HtmlTagType::XML_PARAM: + case HtmlTagType::XML_LIST: + case HtmlTagType::XML_TYPEPARAM: + case HtmlTagType::XML_RETURNS: + case HtmlTagType::XML_SEE: + case HtmlTagType::XML_SEEALSO: + case HtmlTagType::XML_EXCEPTION: + case HtmlTagType::XML_INHERITDOC: + retval = Token::make_RetVal_CloseXml(); break; - case XML_C: + case HtmlTagType::XML_C: parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Code,tagName); break; - case XML_ITEM: - case XML_LISTHEADER: - case XML_INCLUDE: - case XML_PERMISSION: - case XML_DESCRIPTION: - case XML_PARAMREF: - case XML_TYPEPARAMREF: + case HtmlTagType::XML_ITEM: + case HtmlTagType::XML_LISTHEADER: + case HtmlTagType::XML_INCLUDE: + case HtmlTagType::XML_PERMISSION: + case HtmlTagType::XML_DESCRIPTION: + case HtmlTagType::XML_PARAMREF: + case HtmlTagType::XML_TYPEPARAMREF: // These tags are defined in .Net but are currently unsupported break; - case HTML_UNKNOWN: - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag found", qPrint(tagName)); + case HtmlTagType::UNKNOWN: + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag found", tagName); children().append(parser(),thisVariant(),""); break; default: // we should not get here! - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag %s",qPrint(tagName)); + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName); ASSERT(0); break; } - AUTO_TRACE_EXIT("retval={}",DocTokenizer::retvalToString(retval)); + AUTO_TRACE_EXIT("retval={}",retval.to_string()); return retval; } @@ -5341,36 +5720,36 @@ static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser,const DocNodeVariant return false; } -int DocPara::parse() +Token DocPara::parse() { AUTO_TRACE(); auto ns = AutoNodeStack(parser(),thisVariant()); // handle style commands "inherited" from the previous paragraph parser()->handleInitialStyleCommands(thisVariant(),children()); - int tok = 0; - int retval = 0; - while ((tok=parser()->tokenizer.lex())) // get the next token + Token tok=parser()->tokenizer.lex(); + Token retval = Token::make_TK_NONE(); + while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token { reparsetoken: - AUTO_TRACE_ADD("token '{}' at {}",DocTokenizer::tokToString(tok),parser()->tokenizer.getLineNr()); - if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || - tok==TK_COMMAND_AT || tok == TK_COMMAND_BS || tok==TK_HTMLTAG + AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr()); + if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL, + TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG) ) { AUTO_TRACE_ADD("name={}",parser()->context.token->name); } - switch(tok) + switch(tok.value()) { - case TK_WORD: + case TokenRetval::TK_WORD: children().append(parser(),thisVariant(),parser()->context.token->name); break; - case TK_LNKWORD: + case TokenRetval::TK_LNKWORD: parser()->handleLinkedWord(thisVariant(),children()); break; - case TK_URL: + case TokenRetval::TK_URL: children().append(parser(),thisVariant(),parser()->context.token->name,parser()->context.token->isEMailAddr); break; - case TK_WHITESPACE: + case TokenRetval::TK_WHITESPACE: { // prevent leading whitespace and collapse multiple whitespace areas if (insidePRE(thisVariant()) || // all whitespace is relevant @@ -5388,7 +5767,7 @@ int DocPara::parse() } } break; - case TK_LISTITEM: + case TokenRetval::TK_LISTITEM: { AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent); const DocNodeVariant *n=parent(); @@ -5400,7 +5779,7 @@ int DocPara::parse() if (al->indent()>=parser()->context.token->indent) // new item at the same or lower indent level { - retval=TK_LISTITEM; + retval = Token::make_TK_LISTITEM(); goto endparagraph; } } @@ -5424,12 +5803,12 @@ int DocPara::parse() parser()->context.token->isCheckedList); al = children().get_last(); retval = children().get_last()->parse(); - } while (retval==TK_LISTITEM && // new list + } while (retval.is(TokenRetval::TK_LISTITEM) && // new list al->indent()==parser()->context.token->indent // at same indent level ); // check the return value - if (retval==RetVal_SimpleSec) // auto list ended due to simple section command + if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command { // Reparse the token that ended the section at this level, // so a new simple section will be started at this level. @@ -5439,16 +5818,16 @@ int DocPara::parse() { parser()->context.token->name = parser()->context.token->name.mid(4); parser()->context.token->text = parser()->context.token->simpleSectText; - tok = TK_RCSTAG; + tok = Token::make_TK_RCSTAG(); } else // other section { - tok = TK_COMMAND_BS; + tok = Token::make_TK_COMMAND_BS(); } AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name); goto reparsetoken; } - else if (retval==TK_ENDLIST) + else if (retval.is(TokenRetval::TK_ENDLIST)) { if (al->indent()>parser()->context.token->indent) // end list { @@ -5458,13 +5837,13 @@ int DocPara::parse() { } } - else // paragraph ended due to TK_NEWPARA, TK_LISTITEM, or EOF + else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF { goto endparagraph; } } break; - case TK_ENDLIST: + case TokenRetval::TK_ENDLIST: AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr()); if (std::get_if(parent())) { @@ -5472,7 +5851,7 @@ int DocPara::parse() if (al && al->indent()>=parser()->context.token->indent) { // end of list marker ends this paragraph - retval=TK_ENDLIST; + retval = Token::make_TK_ENDLIST(); goto endparagraph; } else @@ -5487,26 +5866,26 @@ int DocPara::parse() "list items"); } break; - case TK_COMMAND_AT: + case TokenRetval::TK_COMMAND_AT: // fall through - case TK_COMMAND_BS: + case TokenRetval::TK_COMMAND_BS: { // see if we have to start a simple section - int cmd = Mappers::cmdMapper->map(parser()->context.token->name); + CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name); const DocNodeVariant *n=parent(); while (n && !std::holds_alternative(*n) && !std::holds_alternative(*n)) { n=::parent(n); } - if (cmd&SIMPLESECT_BIT) + if (cmd>CommandType::SIMPLESECT_BIT) { if (n) // already in a simple section { // simple section cannot start in this paragraph, need // to unwind the stack and remember the command. parser()->context.token->simpleSectName = parser()->context.token->name; - retval=RetVal_SimpleSec; + retval = Token::make_RetVal_SimpleSec(); goto endparagraph; } } @@ -5515,19 +5894,19 @@ int DocPara::parse() while (n && !std::holds_alternative(*n)) n=::parent(n); if (n) { - if (cmd==CMD_LI) + if (cmd==CommandType::CMD_LI) { - retval=RetVal_ListItem; + retval = Token::make_RetVal_ListItem(); goto endparagraph; } } // handle the command - retval=handleCommand(TK_COMMAND_CHAR(tok),parser()->context.token->name); - AUTO_TRACE_ADD("handleCommand returns {}",DocTokenizer::retvalToString(retval)); + retval=handleCommand(tok.command_to_char(),parser()->context.token->name); + AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string()); // check the return value - if (retval==RetVal_SimpleSec) + if (retval.is(TokenRetval::RetVal_SimpleSec)) { // Reparse the token that ended the section at this level, // so a new simple section will be started at this level. @@ -5537,29 +5916,29 @@ int DocPara::parse() { parser()->context.token->name = parser()->context.token->name.mid(4); parser()->context.token->text = parser()->context.token->simpleSectText; - tok = TK_RCSTAG; + tok = Token::make_TK_RCSTAG(); } else // other section { - tok = TK_COMMAND_BS; + tok = Token::make_TK_COMMAND_BS(); } AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name); goto reparsetoken; } - else if (retval>0 && retvalTokenRetval::TK_NONE && retval.value()context.token->endTag) // found a start tag { @@ -5573,13 +5952,13 @@ int DocPara::parse() } retval = handleHtmlEndTag(parser()->context.token->name); } - if (retval!=RetVal_OK) + if (!retval.is(TokenRetval::RetVal_OK)) { goto endparagraph; } } break; - case TK_SYMBOL: + case TokenRetval::TK_SYMBOL: { HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name); if (s!=HtmlEntityMapper::Sym_Unknown) @@ -5589,15 +5968,15 @@ int DocPara::parse() else { children().append(parser(),thisVariant(),parser()->context.token->name); - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '%s' found", - qPrint(parser()->context.token->name)); + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found", + parser()->context.token->name); } break; } - case TK_NEWPARA: - retval=TK_NEWPARA; + case TokenRetval::TK_NEWPARA: + retval = Token::make_TK_NEWPARA(); goto endparagraph; - case TK_RCSTAG: + case TokenRetval::TK_RCSTAG: { const DocNodeVariant *n=parent(); while (n && !std::holds_alternative(*n) && @@ -5611,7 +5990,7 @@ int DocPara::parse() // to unwind the stack and remember the command. parser()->context.token->simpleSectName = "rcs:"+parser()->context.token->name; parser()->context.token->simpleSectText = parser()->context.token->text; - retval=RetVal_SimpleSec; + retval = Token::make_RetVal_SimpleSec(); goto endparagraph; } @@ -5622,33 +6001,34 @@ int DocPara::parse() break; default: warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(), - "Found unexpected token (id=%s)",DocTokenizer::tokToString(tok)); + "Found unexpected token (id={})",tok.to_string()); break; } + tok=parser()->tokenizer.lex(); } - retval=0; + retval=Token::make_TK_NONE(); endparagraph: parser()->handlePendingStyleCommands(thisVariant(),children()); DocPara *par = std::get_if(parser()->context.nodeStack.top()); if (!parser()->context.token->endTag && par && - retval==TK_NEWPARA && parser()->context.token->name.lower() == "p") + retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p") { par->setAttribs(parser()->context.token->attribs); } - INTERNAL_ASSERT(retval==0 || retval==TK_NEWPARA || retval==TK_LISTITEM || - retval==TK_ENDLIST || retval>RetVal_OK - ); + INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM, + TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK) + ); - AUTO_TRACE_EXIT("retval={}",DocTokenizer::retvalToString(retval)); + AUTO_TRACE_EXIT("retval={}",retval.to_string()); return retval; } //-------------------------------------------------------------------------- -int DocSection::parse() +Token DocSection::parse() { AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level); - int retval=RetVal_OK; + Token retval = Token::make_RetVal_OK(); auto ns = AutoNodeStack(parser(),thisVariant()); if (!m_id.isEmpty()) @@ -5684,37 +6064,32 @@ int DocSection::parse() { children().pop_back(); } - if (retval==TK_LISTITEM) + if (retval.is(TokenRetval::TK_LISTITEM)) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found"); } - if (retval==RetVal_Internal) + if (retval.is(TokenRetval::RetVal_Internal)) { children().append(parser(),thisVariant()); retval = children().get_last()->parse(m_level+1); - if (retval==RetVal_EndInternal) + if (retval.is(TokenRetval::RetVal_EndInternal)) { - retval=RetVal_OK; + retval = Token::make_RetVal_OK(); } } - } while (retval!=0 && - retval!=RetVal_Section && - retval!=RetVal_Subsection && - retval!=RetVal_Subsubsection && - retval!=RetVal_Paragraph && - retval!=RetVal_SubParagraph && - retval!=RetVal_SubSubParagraph && - retval!=RetVal_EndInternal + } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, + TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, + TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal) ); if (lastPar) lastPar->markLast(); while (true) { - if (retval==RetVal_Subsection && m_level<=1) + if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1) { // then parse any number of nested sections - while (retval==RetVal_Subsection) // more sections follow + while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow { children().append(parser(),thisVariant(), 2, @@ -5723,84 +6098,83 @@ int DocSection::parse() } break; } - else if (retval==RetVal_Subsubsection && m_level<=2) + else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2) { if ((m_level <= 1) && - AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) + !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) { warn_doc_error(parser()->context.fileName, parser()->tokenizer.getLineNr(), - "Unexpected subsubsection command found inside %s!", + "Unexpected subsubsection command found inside {}!", g_sectionLevelToName[m_level]); } // then parse any number of nested sections - while (retval==RetVal_Subsubsection) // more sections follow + while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow { children().append(parser(),thisVariant(), 3, parser()->context.token->sectionId); retval = children().get_last()->parse(); } - if (!(m_level < 2 && retval == RetVal_Subsection)) break; + if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break; } - else if (retval==RetVal_Paragraph && m_level<=3) + else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3) { if ((m_level <= 2) && - AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) + !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(), - "Unexpected paragraph command found inside %s!", + "Unexpected paragraph command found inside {}!", g_sectionLevelToName[m_level]); } // then parse any number of nested sections - while (retval==RetVal_Paragraph) // more sections follow + while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow { children().append(parser(),thisVariant(), 4, parser()->context.token->sectionId); retval = children().get_last()->parse(); } - if (!(m_level<3 && (retval == RetVal_Subsection || retval == RetVal_Subsubsection))) break; + if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break; } - else if (retval==RetVal_SubParagraph && m_level<=4) + else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4) { if ((m_level <= 3) && - AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) + !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(), - "Unexpected subparagraph command found inside %s!", + "Unexpected subparagraph command found inside {}!", g_sectionLevelToName[m_level]); } // then parse any number of nested sections - while (retval==RetVal_SubParagraph) // more sections follow + while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow { children().append(parser(),thisVariant(), 5, parser()->context.token->sectionId); retval = children().get_last()->parse(); } - if (!(m_level<4 && (retval == RetVal_Subsection || retval == RetVal_Subsubsection || - retval == RetVal_Paragraph))) break; + if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break; } - else if (retval==RetVal_SubSubParagraph && m_level<=5) + else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5) { if ((m_level <= 4) && - AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) + !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(), - "Unexpected subsubparagraph command found inside %s!", + "Unexpected subsubparagraph command found inside {}!", g_sectionLevelToName[m_level]); } // then parse any number of nested sections - while (retval==RetVal_SubSubParagraph) // more sections follow + while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow { children().append(parser(),thisVariant(), 6, parser()->context.token->sectionId); retval = children().get_last()->parse(); } - if (!(m_level<5 && (retval == RetVal_Subsection || retval == RetVal_Subsubsection || - retval == RetVal_Paragraph || retval == RetVal_SubParagraph))) break; + if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection, + TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break; } else { @@ -5808,18 +6182,14 @@ int DocSection::parse() } } - INTERNAL_ASSERT(retval==0 || - retval==RetVal_Section || - retval==RetVal_Subsection || - retval==RetVal_Subsubsection || - retval==RetVal_Paragraph || - retval==RetVal_SubParagraph || - retval==RetVal_SubSubParagraph || - retval==RetVal_Internal || - retval==RetVal_EndInternal + INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, + TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, + TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, + TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph, + TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal) ); - AUTO_TRACE_EXIT("retval={}", DocTokenizer::retvalToString(retval)); + AUTO_TRACE_EXIT("retval={}", retval.to_string()); return retval; } @@ -5831,18 +6201,18 @@ void DocText::parse() auto ns = AutoNodeStack(parser(),thisVariant()); parser()->tokenizer.setStateText(); - int tok = 0; - while ((tok=parser()->tokenizer.lex())) // get the next token + Token tok = parser()->tokenizer.lex(); + while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token { - switch(tok) + switch(tok.value()) { - case TK_WORD: - children().append(parser(),thisVariant(),parser()->context.token->name); - break; - case TK_WHITESPACE: + case TokenRetval::TK_WORD: + children().append(parser(),thisVariant(),parser()->context.token->name); + break; + case TokenRetval::TK_WHITESPACE: children().append(parser(),thisVariant(),parser()->context.token->chars); - break; - case TK_SYMBOL: + break; + case TokenRetval::TK_SYMBOL: { HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name); if (s!=HtmlEntityMapper::Sym_Unknown) @@ -5851,78 +6221,85 @@ void DocText::parse() } else { - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '%s' found", - qPrint(parser()->context.token->name)); + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found", + parser()->context.token->name); } } break; - case TK_COMMAND_AT: + case TokenRetval::TK_COMMAND_AT: // fall through - case TK_COMMAND_BS: + case TokenRetval::TK_COMMAND_BS: switch (Mappers::cmdMapper->map(parser()->context.token->name)) { - case CMD_BSLASH: + case CommandType::CMD_BSLASH: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_BSlash); break; - case CMD_AT: + case CommandType::CMD_AT: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_At); break; - case CMD_LESS: + case CommandType::CMD_LESS: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Less); break; - case CMD_GREATER: + case CommandType::CMD_GREATER: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Greater); break; - case CMD_AMP: + case CommandType::CMD_AMP: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Amp); break; - case CMD_DOLLAR: + case CommandType::CMD_DOLLAR: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Dollar); break; - case CMD_HASH: + case CommandType::CMD_HASH: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Hash); break; - case CMD_DCOLON: + case CommandType::CMD_DCOLON: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_DoubleColon); break; - case CMD_PERCENT: + case CommandType::CMD_PERCENT: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Percent); break; - case CMD_NDASH: + case CommandType::CMD_NDASH: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); break; - case CMD_MDASH: + case CommandType::CMD_MDASH: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); break; - case CMD_QUOTE: + case CommandType::CMD_QUOTE: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Quot); break; - case CMD_PUNT: + case CommandType::CMD_PUNT: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Dot); break; - case CMD_PLUS: + case CommandType::CMD_EXCLAMATION: + children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Exclam); + break; + case CommandType::CMD_QUESTION: + children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Quest); + break; + case CommandType::CMD_PLUS: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Plus); break; - case CMD_MINUS: + case CommandType::CMD_MINUS: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Minus); break; - case CMD_EQUAL: + case CommandType::CMD_EQUAL: children().append(parser(),thisVariant(),HtmlEntityMapper::Sym_Equal); break; default: - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '%s' found", - qPrint(parser()->context.token->name)); + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found", + parser()->context.token->name); break; } break; default: - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token %s", - DocTokenizer::tokToString(tok)); + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}", + tok.to_string()); break; } + tok = parser()->tokenizer.lex(); } parser()->handleUnclosedStyleCommands(); @@ -5937,7 +6314,7 @@ void DocRoot::parse() AUTO_TRACE(); auto ns = AutoNodeStack(parser(),thisVariant()); parser()->tokenizer.setStatePara(); - int retval=0; + Token retval = Token::make_TK_NONE(); // first parse any number of paragraphs bool isFirst=TRUE; @@ -5958,15 +6335,15 @@ void DocRoot::parse() lastPar = par; } } - auto checkParagraph = [this,&retval](Tokens t,int level,const char *sectionType,const char *parentSectionType) { + auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) { if (retval == t) { if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str())) { warn_doc_error(parser()->context.fileName, parser()->tokenizer.getLineNr(), - "found %s command (id: '%s') outside of %s context!", - sectionType,qPrint(parser()->context.token->sectionId),parentSectionType); + "found {} command (id: '{}') outside of {} context!", + sectionType,parser()->context.token->sectionId,parentSectionType); } while (retval==t) { @@ -5982,40 +6359,40 @@ void DocRoot::parse() } else { - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid %s id '%s'; ignoring %s", - sectionType,qPrint(parser()->context.token->sectionId),sectionType); - retval = 0; + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}", + sectionType,parser()->context.token->sectionId,sectionType); + retval = Token::make_TK_NONE(); } } else { - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for %s; ignoring %s",sectionType,sectionType); - retval = 0; + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType); + retval = Token::make_TK_NONE(); } } } }; - checkParagraph(RetVal_SubSubParagraph, 6, "subsubparagraph", "subparagraph" ); - checkParagraph(RetVal_SubParagraph, 5, "subparagraph", "paragraph" ); - checkParagraph(RetVal_Paragraph, 4, "paragraph", "subsubsection" ); - checkParagraph(RetVal_Subsubsection, 3, "subsubsection", "subsection" ); - checkParagraph(RetVal_Subsection, 2, "subsection", "section" ); + checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" ); + checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" ); + checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" ); + checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" ); + checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" ); - if (retval==TK_LISTITEM) + if (retval.is(TokenRetval::TK_LISTITEM)) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found"); } - if (retval==RetVal_Internal) + if (retval.is(TokenRetval::RetVal_Internal)) { children().append(parser(),thisVariant()); retval = children().get_last()->parse(1); } - } while (retval!=0 && retval!=RetVal_Section); + } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section)); if (lastPar) lastPar->markLast(); - //printf("DocRoot::parse() retval=%d %d\n",retval,RetVal_Section); + //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section); // then parse any number of level1 sections - while (retval==RetVal_Section) + while (retval.is(TokenRetval::RetVal_Section)) { if (!parser()->context.token->sectionId.isEmpty()) { @@ -6029,14 +6406,14 @@ void DocRoot::parse() } else { - warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '%s'; ignoring section",qPrint(parser()->context.token->sectionId)); - retval = 0; + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId); + retval = Token::make_TK_NONE(); } } else { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section"); - retval = 0; + retval = Token::make_TK_NONE(); } } diff --git a/src/docnode.h b/src/docnode.h index e700dfb1c6d..64943ccde1a 100644 --- a/src/docnode.h +++ b/src/docnode.h @@ -31,6 +31,8 @@ #include "growvector.h" #include "section.h" #include "construct.h" +#include "doctokenizer.h" +#include "cite.h" class MemberDef; class Definition; @@ -50,7 +52,7 @@ class DocParser; /* 40 */ DN(DocSimpleSect) DN_SEP DN(DocSimpleSectSep) DN_SEP DN(DocParamSect) DN_SEP DN(DocPara) DN_SEP DN(DocParamList) DN_SEP \ /* 45 */ DN(DocSimpleListItem) DN_SEP DN(DocHtmlListItem) DN_SEP DN(DocHtmlDescData) DN_SEP DN(DocHtmlCell) DN_SEP DN(DocHtmlCaption) DN_SEP \ /* 50 */ DN(DocHtmlRow) DN_SEP DN(DocHtmlTable) DN_SEP DN(DocHtmlBlockQuote) DN_SEP DN(DocText) DN_SEP DN(DocRoot) DN_SEP \ -/* 55 */ DN(DocHtmlDetails) DN_SEP DN(DocHtmlSummary) \ +/* 55 */ DN(DocHtmlDetails) DN_SEP DN(DocHtmlSummary) DN_SEP DN(DocPlantUmlFile) \ // forward declarations #define DN(x) class x; @@ -242,19 +244,22 @@ class DocAnchor : public DocNode class DocCite : public DocNode { public: - DocCite(DocParser *parser,DocNodeVariant *parent,const QCString &target,const QCString &context); + DocCite(DocParser *parser,DocNodeVariant *parent,const QCString &target,const QCString &context, CiteInfoOption opt); QCString file() const { return m_file; } QCString relPath() const { return m_relPath; } QCString ref() const { return m_ref; } QCString anchor() const { return m_anchor; } - QCString text() const { return m_text; } + QCString target() const { return m_target; } + CiteInfoOption option() const { return m_option; } + QCString getText() const; private: QCString m_file; QCString m_relPath; QCString m_ref; QCString m_anchor; - QCString m_text; + QCString m_target; + CiteInfoOption m_option; }; @@ -277,7 +282,9 @@ class DocStyleChange : public DocNode Del = (1<<12), Ins = (1<<13), S = (1<<14), - Cite = (1<<15) + Cite = (1<<15), + Kbd = (1<<16), + Typewriter = (1<<17) }; DocStyleChange(DocParser *parser,DocNodeVariant *parent,size_t position,Style s, @@ -287,12 +294,24 @@ class DocStyleChange : public DocNode if (attribs) m_attribs=*attribs; m_tagName = tagName.lower(); } + DocStyleChange(DocParser *parser,DocNodeVariant *parent,size_t position,Style s, + const QCString &tagName,bool enable, + const QCString &fileName,int lineNr, + const HtmlAttribList *attribs=nullptr) + : DocNode(parser,parent), m_position(position), m_style(s), m_enable(enable), + m_fileName(fileName), m_lineNr(lineNr) + { + if (attribs) m_attribs=*attribs; + m_tagName = tagName.lower(); + } Style style() const { return m_style; } const char *styleString() const; bool enable() const { return m_enable; } size_t position() const { return m_position; } const HtmlAttribList &attribs() const { return m_attribs; } QCString tagName() const { return m_tagName; } + QCString fileName() const { return m_fileName; } + int lineNr() const { return m_lineNr; } private: size_t m_position = 0; @@ -300,6 +319,8 @@ class DocStyleChange : public DocNode bool m_enable = false; HtmlAttribList m_attribs; QCString m_tagName; + QCString m_fileName; + int m_lineNr = -1; }; /** Node representing a special symbol */ @@ -415,14 +436,15 @@ class DocInclude : public DocNode public: enum Type { Include, DontInclude, VerbInclude, HtmlInclude, LatexInclude, IncWithLines, Snippet , SnippetWithLines, - DontIncWithLines, RtfInclude, ManInclude, DocbookInclude, XmlInclude, - SnippetTrimLeft}; + DontIncWithLines, RtfInclude, ManInclude, DocbookInclude, XmlInclude + }; DocInclude(DocParser *parser,DocNodeVariant *parent,const QCString &file, - const QCString &context, Type t, + const QCString &context, Type t, bool stripCodeComments, bool isExample,const QCString &exampleFile, - const QCString &blockId, bool isBlock) + const QCString &blockId, bool isBlock, bool trimLeft) : DocNode(parser,parent), m_file(file), m_context(context), m_type(t), - m_isExample(isExample), m_isBlock(isBlock), + m_stripCodeComments(stripCodeComments), + m_isExample(isExample), m_isBlock(isBlock), m_trimLeft(trimLeft), m_exampleFile(exampleFile), m_blockId(blockId) {} QCString file() const { return m_file; } QCString extension() const { int i=m_file.findRev('.'); return i!=-1 ? m_file.mid(i) : QCString(); } @@ -430,9 +452,11 @@ class DocInclude : public DocNode QCString text() const { return m_text; } QCString context() const { return m_context; } QCString blockId() const { return m_blockId; } + bool stripCodeComments() const { return m_stripCodeComments; } bool isExample() const { return m_isExample; } QCString exampleFile() const { return m_exampleFile; } bool isBlock() const { return m_isBlock; } + bool trimLeft() const { return m_trimLeft; } void parse(); private: @@ -440,8 +464,10 @@ class DocInclude : public DocNode QCString m_context; QCString m_text; Type m_type; + bool m_stripCodeComments; bool m_isExample; bool m_isBlock; + bool m_trimLeft; QCString m_exampleFile; QCString m_blockId; }; @@ -452,9 +478,9 @@ class DocIncOperator : public DocNode public: enum Type { Line, SkipLine, Skip, Until }; DocIncOperator(DocParser *parser,DocNodeVariant *parent,Type t,const QCString &pat, - const QCString &context,bool isExample,const QCString &exampleFile) + const QCString &context,bool stripCodeComments,bool isExample,const QCString &exampleFile) : DocNode(parser,parent), m_type(t), m_pattern(pat), m_context(context), - m_isFirst(FALSE), m_isLast(FALSE), + m_isFirst(FALSE), m_isLast(FALSE), m_stripCodeComments(stripCodeComments), m_isExample(isExample), m_exampleFile(exampleFile) {} Type type() const { return m_type; } const char *typeAsString() const @@ -477,6 +503,7 @@ class DocIncOperator : public DocNode bool isLast() const { return m_isLast; } void markFirst(bool v=TRUE) { m_isFirst = v; } void markLast(bool v=TRUE) { m_isLast = v; } + bool stripCodeComments() const { return m_stripCodeComments; } bool isExample() const { return m_isExample; } QCString exampleFile() const { return m_exampleFile; } QCString includeFileName() const { return m_includeFileName; } @@ -491,6 +518,7 @@ class DocIncOperator : public DocNode QCString m_context; bool m_isFirst = false; bool m_isLast = false; + bool m_stripCodeComments = true; bool m_isExample = false; QCString m_exampleFile; QCString m_includeFileName; @@ -525,7 +553,7 @@ class DocIndexEntry : public DocNode public: DocIndexEntry(DocParser *parser,DocNodeVariant *parent,const Definition *scope,const MemberDef *md) : DocNode(parser,parent), m_scope(scope), m_member(md) {} - int parse(); + Token parse(); const Definition *scope() const { return m_scope; } const MemberDef *member() const { return m_member; } QCString entry() const { return m_entry; } @@ -553,7 +581,7 @@ class DocAutoList : public DocCompoundNode int indent() const { return m_indent; } bool isCheckedList() const { return m_isCheckedList; } int depth() const { return m_depth; } - int parse(); + Token parse(); private: int m_indent = 0; @@ -568,7 +596,7 @@ class DocAutoListItem : public DocCompoundNode public: DocAutoListItem(DocParser *parser,DocNodeVariant *parent,int indent,int num); int itemNumber() const { return m_itemNum; } - int parse(); + Token parse(); private: int m_indent = 0; @@ -583,6 +611,7 @@ class DocTitle : public DocCompoundNode void parse(); void parseFromString(DocNodeVariant *,const QCString &title); bool hasTitle() const { return !children().empty(); } + bool isEmpty() const { return !hasTitle(); } private: }; @@ -706,6 +735,15 @@ class DocDiaFile : public DocDiagramFileBase bool parse(); }; +/** Node representing a uml file */ +class DocPlantUmlFile : public DocDiagramFileBase +{ + public: + DocPlantUmlFile(DocParser *parser,DocNodeVariant *parent,const QCString &name,const QCString &context, + const QCString &srcFile,int srcLine); + bool parse(); +}; + /** Node representing a VHDL flow chart */ class DocVhdlFlow : public DocCompoundNode { @@ -788,7 +826,7 @@ class DocHRef : public DocCompoundNode const QCString &relPath, const QCString &file) : DocCompoundNode(parser,parent), m_attribs(attribs), m_http://github.com/url(url), m_relPath(relPath), m_file(file) {} - int parse(); + Token parse(); QCString url() const { return m_url; } QCString file() const { return m_file; } QCString relPath() const { return m_relPath; } @@ -821,7 +859,7 @@ class DocHtmlDetails : public DocCompoundNode DocHtmlDetails(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs) : DocCompoundNode(parser,parent), m_attribs(attribs) {} const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); void parseSummary(DocNodeVariant *,HtmlAttribList &attribs); const DocNodeVariant *summary() const { return m_summary.get(); } @@ -838,7 +876,7 @@ class DocHtmlHeader : public DocCompoundNode DocCompoundNode(parser,parent), m_level(level), m_attribs(attribs) {} int level() const { return m_level; } const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); private: int m_level = 0; @@ -852,7 +890,7 @@ class DocHtmlDescTitle : public DocCompoundNode DocHtmlDescTitle(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs) : DocCompoundNode(parser,parent), m_attribs(attribs) {} const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); private: HtmlAttribList m_attribs; @@ -865,7 +903,7 @@ class DocHtmlDescList : public DocCompoundNode DocHtmlDescList(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs) : DocCompoundNode(parser,parent), m_attribs(attribs) {} const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); private: HtmlAttribList m_attribs; @@ -882,7 +920,7 @@ class DocSection : public DocCompoundNode QCString anchor() const { return m_anchor; } QCString id() const { return m_id; } QCString file() const { return m_file; } - int parse(); + Token parse(); private: int m_level = 0; @@ -931,7 +969,7 @@ class DocInternal : public DocCompoundNode { public: DocInternal(DocParser *parser,DocNodeVariant *parent) : DocCompoundNode(parser,parent) {} - int parse(int); + Token parse(int); private: }; @@ -941,7 +979,7 @@ class DocParBlock : public DocCompoundNode { public: DocParBlock(DocParser *parser,DocNodeVariant *parent) : DocCompoundNode(parser,parent) {} - int parse(); + Token parse(); private: }; @@ -952,7 +990,7 @@ class DocSimpleList : public DocCompoundNode { public: DocSimpleList(DocParser *parser,DocNodeVariant *parent) : DocCompoundNode(parser,parent) {} - int parse(); + Token parse(); private: }; @@ -966,8 +1004,8 @@ class DocHtmlList : public DocCompoundNode DocCompoundNode(parser,parent), m_type(t), m_attribs(attribs) {} Type type() const { return m_type; } const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); - int parseXml(); + Token parse(); + Token parseXml(); private: Type m_type = Unordered; @@ -987,9 +1025,9 @@ class DocSimpleSect : public DocCompoundNode DocSimpleSect(DocParser *parser,DocNodeVariant *parent,Type t); Type type() const { return m_type; } QCString typeString() const; - int parse(bool userTitle,bool needsSeparator); - int parseRcs(); - int parseXml(); + Token parse(bool userTitle,bool needsSeparator); + Token parseRcs(); + Token parseXml(); void appendLinkWord(const QCString &word); bool hasTitle() const; const DocNodeVariant *title() const { return m_title.get(); } @@ -1026,7 +1064,7 @@ class DocParamSect : public DocCompoundNode DocParamSect(DocParser *parser,DocNodeVariant *parent,Type t) : DocCompoundNode(parser,parent), m_type(t), m_hasInOutSpecifier(FALSE), m_hasTypeSpecifier(FALSE) {} - int parse(const QCString &cmdName,bool xmlContext,Direction d); + Token parse(const QCString &cmdName,bool xmlContext,Direction d); Type type() const { return m_type; } bool hasInOutSpecifier() const { return m_hasInOutSpecifier; } bool hasTypeSpecifier() const { return m_hasTypeSpecifier; } @@ -1042,19 +1080,19 @@ class DocPara : public DocCompoundNode { public: DocPara(DocParser *parser,DocNodeVariant *parent); - int parse(); + Token parse(); bool isEmpty() const { return children().empty(); } void markFirst(bool v=TRUE) { m_isFirst=v; } void markLast(bool v=TRUE) { m_isLast=v; } bool isFirst() const { return m_isFirst; } bool isLast() const { return m_isLast; } - int handleCommand(char cmdChar,const QCString &cmdName); - int handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs); - int handleHtmlEndTag(const QCString &tagName); - int handleSimpleSection(DocSimpleSect::Type t,bool xmlContext=FALSE); - int handleXRefItem(); - int handleParamSection(const QCString &cmdName,DocParamSect::Type t, bool xmlContext, int direction); + Token handleCommand(char cmdChar,const QCString &cmdName); + Token handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs); + Token handleHtmlEndTag(const QCString &tagName); + Token handleSimpleSection(DocSimpleSect::Type t,bool xmlContext=FALSE); + Token handleXRefItem(); + Token handleParamSection(const QCString &cmdName,DocParamSect::Type t, bool xmlContext, int direction); void handleIncludeOperator(const QCString &cmdName,DocIncOperator::Type t); template void handleFile(const QCString &cmdName); void handleInclude(const QCString &cmdName,DocInclude::Type t); @@ -1069,10 +1107,10 @@ class DocPara : public DocCompoundNode void handleILine(char cmdChar,const QCString &cmdName); void handleIFile(char cmdChar,const QCString &cmdName); void handleShowDate(char cmdChar,const QCString &cmdName); - int handleStartCode(); - int handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level); + Token handleStartCode(); + Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level); - bool injectToken(int tok,const QCString &tokText); + bool injectToken(Token tok,const QCString &tokText); const HtmlAttribList &attribs() const { return m_attribs; } void setAttribs(const HtmlAttribList &attribs) { m_attribs = attribs; } @@ -1097,8 +1135,8 @@ class DocParamList : public DocNode void markLast(bool b=TRUE) { m_isLast=b; } bool isFirst() const { return m_isFirst; } bool isLast() const { return m_isLast; } - int parse(const QCString &cmdName); - int parseXml(const QCString ¶mName); + Token parse(const QCString &cmdName); + Token parseXml(const QCString ¶mName); private: DocNodeList m_paragraphs; @@ -1115,7 +1153,7 @@ class DocSimpleListItem : public DocNode { public: DocSimpleListItem(DocParser *parser,DocNodeVariant *parent); - int parse(); + Token parse(); const DocNodeVariant *paragraph() const { return m_paragraph.get(); } private: @@ -1130,8 +1168,8 @@ class DocHtmlListItem : public DocCompoundNode : DocCompoundNode(parser,parent), m_attribs(attribs), m_itemNum(num) {} int itemNumber() const { return m_itemNum; } const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); - int parseXml(); + Token parse(); + Token parseXml(); private: HtmlAttribList m_attribs; @@ -1144,7 +1182,7 @@ class DocHtmlDescData : public DocCompoundNode public: DocHtmlDescData(DocParser *parser,DocNodeVariant *parent) : DocCompoundNode(parser,parent) {} const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); private: HtmlAttribList m_attribs; @@ -1165,8 +1203,8 @@ class DocHtmlCell : public DocCompoundNode void markFirst(bool v=TRUE) { m_isFirst=v; } void markLast(bool v=TRUE) { m_isLast=v; } const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); - int parseXml(); + Token parse(); + Token parseXml(); uint32_t rowIndex() const { return m_rowIdx; } uint32_t columnIndex() const { return m_colIdx; } uint32_t rowSpan() const; @@ -1191,7 +1229,7 @@ class DocHtmlCaption : public DocCompoundNode public: DocHtmlCaption(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs); const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); + Token parse(); bool hasCaptionId() const { return m_hasCaptionId; } QCString file() const { return m_file; } QCString anchor() const { return m_anchor; } @@ -1212,8 +1250,8 @@ class DocHtmlRow : public DocCompoundNode : DocCompoundNode(parser,parent), m_attribs(attribs) {} size_t numCells() const { return children().size(); } const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); - int parseXml(bool header); + Token parse(); + Token parseXml(bool header); bool isHeading() const; void setVisibleCells(uint32_t n) { m_visibleCells = n; } uint32_t visibleCells() const { return m_visibleCells; } @@ -1235,11 +1273,11 @@ class DocHtmlTable : public DocCompoundNode size_t numRows() const { return children().size(); } bool hasCaption() const; const HtmlAttribList &attribs() const { return m_attribs; } - int parse(); - int parseXml(); + Token parse(); + Token parseXml(); size_t numColumns() const { return m_numCols; } const DocNodeVariant *caption() const; - const DocNodeVariant *firstRow() const; + size_t numberHeaderRows() const; private: void computeTableGrid(); @@ -1254,7 +1292,7 @@ class DocHtmlBlockQuote : public DocCompoundNode public: DocHtmlBlockQuote(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs) : DocCompoundNode(parser,parent), m_attribs(attribs) {} - int parse(); + Token parse(); const HtmlAttribList &attribs() const { return m_attribs; } private: @@ -1444,6 +1482,10 @@ class DocNodeAST : public IDocNodeAST { return std::get(root).isEmpty(); } + else if (std::holds_alternative(root)) + { + return std::get(root).isEmpty(); + } return false; } DocNodeVariant root; diff --git a/src/docoptions.h b/src/docoptions.h new file mode 100644 index 00000000000..77cf712731b --- /dev/null +++ b/src/docoptions.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2025 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef DOCOPTIONS_H +#define DOCOPTIONS_H + +#include "config.h" +#include "qcstring.h" + +struct DocOptions +{ + public: + // === getters for optional params + bool indexWords() const { return m_indexWords; } + bool isExample() const { return m_isExample; } + QCString exampleName() const { return m_exampleName; } + bool singleLine() const { return m_singleLine; } + bool linkFromIndex() const { return m_linkFromIndex; } + bool markdownSupport() const { return m_markdownSupport; } + bool autolinkSupport() const { return m_autolinkSupport; } + int sectionLevel() const { return m_sectionLevel; } + + // === setters for optional params + + /// Indicates whether or not words should be put in the search index. + DocOptions &setIndexWords(bool b) + { m_indexWords=b; return *this; } + + /// Associate this comment block with a given example + DocOptions &setExample(const QCString &name) + { m_isExample=!name.isEmpty(); m_exampleName = name; return *this; } + + /// Select if this is for a single line, so without starting a new paragraph at the end. + DocOptions &setSingleLine(bool b) + { m_singleLine=b; return *this; } + + /// Select if the documentation is generated from an + /// index page. In this case context is not used to determine + /// the relative path when making a link. + DocOptions &setLinkFromIndex(bool b) + { m_linkFromIndex=b; return *this; } + + /// Select if the input needs to take markdown markup into account. + DocOptions &setMarkdownSupport(bool b) + { m_markdownSupport=b; return *this; } + + /// Select if the input need to perform auto linking of words + DocOptions &setAutolinkSupport(bool b) + { m_autolinkSupport=b; return *this; } + + /// Restrict output up to the given section level + DocOptions &setSectionLevel(int l) + { m_sectionLevel=l; return *this; } + + private: + // optional params with defaults + bool m_indexWords = false; + bool m_isExample = false; + QCString m_exampleName; + bool m_singleLine = false; + bool m_linkFromIndex = false; + bool m_markdownSupport = Config_getBool(MARKDOWN_SUPPORT); + bool m_autolinkSupport = Config_getBool(AUTOLINK_SUPPORT); + int m_sectionLevel = -1; +}; + +#endif + diff --git a/src/docparser.cpp b/src/docparser.cpp index 518aee1c941..7c9074920c4 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -38,6 +38,7 @@ #include "util.h" #include "indexlist.h" #include "trace.h" +#include "stringutil.h" #if !ENABLE_DOCPARSER_TRACING #undef AUTO_TRACE @@ -100,11 +101,9 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty { if (ambig & doWarn) { - QCString text; - text.sprintf("image file name '%s' is ambiguous.\n",qPrint(fileName)); - text+="Possible candidates:\n"; - text+=showFileDefMatches(Doxygen::imageNameLinkedMap,fileName); - warn_doc_error(context.fileName,tokenizer.getLineNr(),"%s", qPrint(text)); + warn_doc_error(context.fileName,tokenizer.getLineNr(), + "image file name '{}' is ambiguous.\n" + "Possible candidates:\n{}", fileName,showFileDefMatches(Doxygen::imageNameLinkedMap,fileName)); } QCString inputFile = fd->absFilePath(); @@ -148,8 +147,8 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty { Dir().remove(outputFile.str()); warn_doc_error(context.fileName,tokenizer.getLineNr(), - "destination of image %s is a symlink, replacing with image", - qPrint(outputFile)); + "destination of image {} is a symlink, replacing with image", + outputFile); } if (outputFile!=inputFile) // prevent copying to ourself { @@ -162,7 +161,7 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty else { warn_doc_error(context.fileName,tokenizer.getLineNr(), - "could not open image %s",qPrint(fileName)); + "could not open image {}",fileName); } if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) && @@ -192,8 +191,8 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty if (!result.startsWith("http:") && !result.startsWith("https:") && doWarn) { warn_doc_error(context.fileName,tokenizer.getLineNr(), - "image file %s is not found in IMAGE_PATH: " - "assuming external image.",qPrint(fileName) + "image file {} is not found in IMAGE_PATH: " + "assuming external image.",fileName ); } } @@ -261,10 +260,10 @@ void DocParser::checkArgumentName() } QCString alStr = argListToString(al); warn_doc_error(docFile,docLine, - "argument '%s' of command @param " - "is not found in the argument list of %s%s%s%s", - qPrint(aName), qPrint(scope), qPrint(context.memberDef->name()), - qPrint(alStr), qPrint(inheritedFrom)); + "argument '{}' of command @param " + "is not found in the argument list of {}{}{}{}", + aName, scope, context.memberDef->name(), + alStr, inheritedFrom); } } } @@ -280,10 +279,8 @@ void DocParser::checkRetvalName() { warn_doc_error(context.memberDef->getDefFileName(), context.memberDef->getDefLine(), - "%s", - qPrint("return value '" + name + "' of " + - QCString(context.memberDef->qualifiedName()) + - " has multiple documentation sections")); + "return value '{}' of {} has multiple documentation sections", + name, context.memberDef->qualifiedName()); } context.retvalsFound.insert(name.str()); } @@ -316,6 +313,10 @@ void DocParser::checkUnOrMultipleDocumentedParams() { // allow undocumented self / cls parameter for Python } + else if (lang==SrcLangExt::Cpp && (a.type=="this" || a.type.startsWith("this "))) + { + // allow undocumented this (for C++23 deducing this), see issue #11123 + } else if (!argName.isEmpty()) { size_t count = context.paramsFound.count(argName.str()); @@ -327,11 +328,8 @@ void DocParser::checkUnOrMultipleDocumentedParams() { warn_doc_error(context.memberDef->docFile(), context.memberDef->docLine(), - "%s", - qPrint("argument '" + aName + - "' from the argument list of " + - QCString(context.memberDef->qualifiedName()) + - " has multiple @param documentation sections")); + "argument {} from the argument list of {} has multiple @param documentation sections", + aName, context.memberDef->qualifiedName()); } } } @@ -340,9 +338,9 @@ void DocParser::checkUnOrMultipleDocumentedParams() bool first=TRUE; QCString errMsg = "The following parameter"; if (undocParams.size()>1) errMsg+="s"; - errMsg+=" of "+ - QCString(context.memberDef->qualifiedName()) + - QCString(argListToString(al)) + + errMsg+=QCString(" of ")+ + context.memberDef->qualifiedName() + + argListToString(al) + (undocParams.size()>1 ? " are" : " is") + " not documented:\n"; for (const Argument &a : undocParams) { @@ -353,10 +351,7 @@ void DocParser::checkUnOrMultipleDocumentedParams() first=FALSE; errMsg+=" parameter '"+argName+"'"; } - warn_incomplete_doc(context.memberDef->docFile(), - context.memberDef->docLine(), - "%s", - qPrint(substitute(errMsg,"%","%%"))); + warn_incomplete_doc(context.memberDef->docFile(), context.memberDef->docLine(), "{}", errMsg); } } else @@ -365,9 +360,8 @@ void DocParser::checkUnOrMultipleDocumentedParams() { warn_doc_error(context.memberDef->docFile(), context.memberDef->docLine(), - "%s", - qPrint(context.memberDef->qualifiedName() + - " has @param documentation sections but no arguments")); + "{} has @param documentation sections but no arguments", + context.memberDef->qualifiedName()); } } } @@ -539,59 +533,57 @@ bool DocParser::findDocsForMemberOrCompound(const QCString &commandName, } //--------------------------------------------------------------------------- -void DocParser::errorHandleDefaultToken(DocNodeVariant *parent,int tok, +void DocParser::errorHandleDefaultToken(DocNodeVariant *parent,Token tok, DocNodeList &children,const QCString &txt) { - switch (tok) + switch (tok.value()) { - case TK_COMMAND_AT: + case TokenRetval::TK_COMMAND_AT: // fall through - case TK_COMMAND_BS: + case TokenRetval::TK_COMMAND_BS: { - std::string str{TK_COMMAND_CHAR(tok)}; - children.append(this,parent,str.c_str() + context.token->name); - warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '%c%s' found as part of a %s", - TK_COMMAND_CHAR(tok),qPrint(context.token->name),qPrint(txt)); + char cs[2] = { tok.command_to_char(), 0 }; + children.append(this,parent,cs + context.token->name); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a {}", + tok.command_to_char(),context.token->name,txt); } break; - case TK_SYMBOL: - warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '%s' found as part of a %s", + case TokenRetval::TK_SYMBOL: + warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a {}", qPrint(context.token->name), qPrint(txt)); break; - case TK_HTMLTAG: - warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <%s%s> found as part of a %s", - context.token->endTag ? "/" : "",qPrint(context.token->name), qPrint(txt)); + case TokenRetval::TK_HTMLTAG: + warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <{}{}> found as part of a {}", + context.token->endTag ? "/" : "",context.token->name, txt); break; default: children.append(this,parent,context.token->name); - warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token %s found as part of a %s", - DocTokenizer::tokToString(tok), qPrint(txt)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token {} found as part of a {}", + tok.to_string(), txt); break; } } //--------------------------------------------------------------------------- -int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,const QCString &cmdName) +Token DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,const QCString &cmdName) { AUTO_TRACE("cmdName={}",cmdName); QCString saveCmdName = cmdName; - int tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + Token tok=tokenizer.lex(); + if (!tok.is(TokenRetval::TK_WHITESPACE)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\%s command", - qPrint(saveCmdName)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", + saveCmdName); return tok; } - while ((tok=tokenizer.lex()) && - tok!=TK_WHITESPACE && - tok!=TK_NEWPARA && - tok!=TK_LISTITEM && - tok!=TK_ENDLIST + tok = tokenizer.lex(); + while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::TK_WHITESPACE, + TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST) ) { static const reg::Ex specialChar(R"([.,|()\[\]:;?])"); - if (tok==TK_WORD && context.token->name.length()==1 && + if (tok.is(TokenRetval::TK_WORD) && context.token->name.length()==1 && reg::match(context.token->name.str(),specialChar)) { // special character that ends the markup command @@ -599,15 +591,18 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children, } if (!defaultHandleToken(parent,tok,children)) { - switch (tok) + switch (tok.value()) { - case TK_HTMLTAG: - if (insideLI(parent) && Mappers::htmlTagMapper->map(context.token->name) && context.token->endTag) - { // ignore as the end of a style command - continue; + case TokenRetval::TK_HTMLTAG: + if (insideLI(parent) && Mappers::htmlTagMapper->map(context.token->name)!=HtmlTagType::UNKNOWN && context.token->endTag) + { + // ignore as the end of a style command + } + else + { + AUTO_TRACE_EXIT("end tok={}",tok.to_string()); + return tok; } - AUTO_TRACE_EXIT("end tok={}",DocTokenizer::tokToString(tok)); - return tok; break; default: errorHandleDefaultToken(parent,tok,children,"\\" + saveCmdName + " command"); @@ -615,10 +610,10 @@ int DocParser::handleStyleArgument(DocNodeVariant *parent,DocNodeList &children, } break; } + tok = tokenizer.lex(); } - AUTO_TRACE_EXIT("end tok={}",DocTokenizer::tokToString(tok)); - return (tok==TK_NEWPARA || tok==TK_LISTITEM || tok==TK_ENDLIST - ) ? tok : RetVal_OK; + AUTO_TRACE_EXIT("end tok={}",tok.to_string()); + return (tok.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST)) ? tok : Token::make_RetVal_OK(); } /*! Called when a style change starts. For instance a \ command is @@ -628,8 +623,10 @@ void DocParser::handleStyleEnter(DocNodeVariant *parent,DocNodeList &children, DocStyleChange::Style s,const QCString &tagName,const HtmlAttribList *attribs) { AUTO_TRACE("tagName={}",tagName); - children.append(this,parent,context.nodeStack.size(),s,tagName,TRUE,attribs); + children.append(this,parent,context.nodeStack.size(),s,tagName,TRUE, + context.fileName,tokenizer.getLineNr(),attribs); context.styleStack.push(&children.back()); + context.inCodeStyle = s==DocStyleChange::Style::Typewriter; } /*! Called when a style change ends. For instance a \ command is @@ -654,23 +651,18 @@ void DocParser::handleStyleLeave(DocNodeVariant *parent,DocNodeList &children, { if (context.styleStack.empty()) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found tag without matching <%s>", - qPrint(tagName),qPrint(tagName)); - } - else if (topStyleChange(context.styleStack).tagName()!=tagNameLower) - { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found tag while expecting ", - qPrint(tagName),qPrint(topStyleChange(context.styleStack).tagName())); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"found tag without matching <{0}>",tagName); } - else if (topStyleChange(context.styleStack).style()!=s) + else if (topStyleChange(context.styleStack).tagName()!=tagNameLower || + topStyleChange(context.styleStack).style()!=s) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found tag while expecting ", - qPrint(tagName),qPrint(topStyleChange(context.styleStack).tagName())); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"found tag while expecting ", + tagName,topStyleChange(context.styleStack).tagName()); } else { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found at different nesting level (%zu) than expected (%zu)", - qPrint(tagName),context.nodeStack.size(),topStyleChange(context.styleStack).position()); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"found at different nesting level ({}) than expected ({})", + tagName,context.nodeStack.size(),topStyleChange(context.styleStack).position()); } } else // end the section @@ -680,6 +672,10 @@ void DocParser::handleStyleLeave(DocNodeVariant *parent,DocNodeList &children, topStyleChange(context.styleStack).tagName(),FALSE); context.styleStack.pop(); } + if (s==DocStyleChange::Style::Typewriter) + { + context.inCodeStyle = false; + } } /*! Called at the end of a paragraph to close all open style changes @@ -714,12 +710,12 @@ void DocParser::handleInitialStyleCommands(DocNodeVariant *parent,DocNodeList &c } } -int DocParser::handleAHref(DocNodeVariant *parent,DocNodeList &children, +Token DocParser::handleAHref(DocNodeVariant *parent,DocNodeList &children, const HtmlAttribList &tagHtmlAttribs) { AUTO_TRACE(); size_t index=0; - int retval = RetVal_OK; + Token retval = Token::make_RetVal_OK(); for (const auto &opt : tagHtmlAttribs) { if (opt.name=="name" || opt.name=="id") // or tag @@ -765,20 +761,36 @@ void DocParser::handleUnclosedStyleCommands() if (!context.initialStyleStack.empty()) { QCString tagName = std::get(*context.initialStyleStack.top()).tagName(); + QCString fileName = std::get(*context.initialStyleStack.top()).fileName(); + int lineNr = std::get(*context.initialStyleStack.top()).lineNr(); context.initialStyleStack.pop(); handleUnclosedStyleCommands(); - warn_doc_error(context.fileName,tokenizer.getLineNr(), - "end of comment block while expecting " - "command ",qPrint(tagName)); + if (lineNr != -1) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(), + "end of comment block while expecting " + "command (Probable start '{}' at line {})",tagName, fileName, lineNr); + } + else + { + warn_doc_error(context.fileName,tokenizer.getLineNr(), + "end of comment block while expecting command ",tagName); + } } } void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag) { + // helper to check if word w starts with any of the words in AUTOLINK_IGNORE_WORDS + auto ignoreWord = [](const QCString &w) -> bool { + const auto &list = Config_getList(AUTOLINK_IGNORE_WORDS); + return std::find_if(list.begin(), list.end(), + [&w](const auto &ignore) { return w.startsWith(ignore); } + )!=list.end(); + }; QCString name = linkToText(context.lang,context.token->name,TRUE); AUTO_TRACE("word={}",name); - bool autolinkSupport = Config_getBool(AUTOLINK_SUPPORT); - if (!autolinkSupport && !ignoreAutoLinkFlag) // no autolinking -> add as normal word + if ((!context.autolinkSupport && !ignoreAutoLinkFlag) || ignoreWord(context.token->name)) // no autolinking -> add as normal word { children.append(this,parent,name); return; @@ -792,22 +804,24 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo ClassDef *cd=nullptr; bool ambig = false; FileDef *fd = findFileDef(Doxygen::inputNameLinkedMap,context.fileName,ambig); + auto lang = context.lang; + bool inSeeBlock = context.inSeeBlock || context.inCodeStyle; //printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context)); if (!context.insideHtmlLink && - (resolveRef(context.context,context.token->name,context.inSeeBlock,&compound,&member,TRUE,fd,TRUE) + (resolveRef(context.context,context.token->name,inSeeBlock,&compound,&member,lang,TRUE,fd,TRUE) || (!context.context.isEmpty() && // also try with global scope - resolveRef(QCString(),context.token->name,context.inSeeBlock,&compound,&member,FALSE,nullptr,TRUE)) + resolveRef(QCString(),context.token->name,inSeeBlock,&compound,&member,lang,FALSE,nullptr,TRUE)) ) ) { - //printf("resolveRef %s = %p (linkable?=%d)\n",qPrint(context.token->name),(void*)member,member ? member->isLinkable() : FALSE); + //printf("ADD %s = %p (linkable?=%d)\n",qPrint(context.token->name),(void*)member,member ? member->isLinkable() : FALSE); if (member && member->isLinkable()) // member link { AUTO_TRACE_ADD("resolved reference as member link"); if (member->isObjCMethod()) { bool localLink = context.memberDef ? member->getClassDef()==context.memberDef->getClassDef() : FALSE; - name = member->objCMethodName(localLink,context.inSeeBlock); + name = member->objCMethodName(localLink,inSeeBlock); } children.append( this,parent,name, @@ -876,9 +890,10 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo } else // normal non-linkable word { + AUTO_TRACE_ADD("non-linkable"); if (context.token->name.startsWith("#")) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '%s' could not be resolved",qPrint(name)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '{}' could not be resolved",name); } children.append(this,parent,context.token->name); } @@ -910,21 +925,20 @@ void DocParser::handleParameterType(DocNodeVariant *parent,DocNodeList &children void DocParser::handleInternalRef(DocNodeVariant *parent,DocNodeList &children) { - int tok=tokenizer.lex(); + Token tok=tokenizer.lex(); QCString tokenName = context.token->name; AUTO_TRACE("name={}",tokenName); - if (tok!=TK_WHITESPACE) + if (!tok.is(TokenRetval::TK_WHITESPACE)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\%s command", - qPrint(tokenName)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", tokenName); return; } tokenizer.setStateInternalRef(); tok=tokenizer.lex(); // get the reference id - if (tok!=TK_WORD && tok!=TK_LNKWORD) + if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token %s as the argument of %s", - DocTokenizer::tokToString(tok),qPrint(tokenName)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}", + tok.to_string(),tokenName); return; } children.append(this,parent,context.token->name); @@ -934,25 +948,25 @@ void DocParser::handleInternalRef(DocNodeVariant *parent,DocNodeList &children) void DocParser::handleAnchor(DocNodeVariant *parent,DocNodeList &children) { AUTO_TRACE(); - int tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + Token tok=tokenizer.lex(); + if (!tok.is(TokenRetval::TK_WHITESPACE)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\%s command", - qPrint(context.token->name)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", + context.token->name); return; } tokenizer.setStateAnchor(); tok=tokenizer.lex(); - if (tok==0) + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) { warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the " - "argument of command %s",qPrint(context.token->name)); + "argument of command {}",context.token->name); return; } - else if (tok!=TK_WORD && tok!=TK_LNKWORD) + else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token %s as the argument of %s", - DocTokenizer::tokToString(tok),qPrint(context.token->name)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}", + tok.to_string(),context.token->name); return; } tokenizer.setStatePara(); @@ -962,25 +976,24 @@ void DocParser::handleAnchor(DocNodeVariant *parent,DocNodeList &children) void DocParser::handlePrefix(DocNodeVariant *parent,DocNodeList &children) { AUTO_TRACE(); - int tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + Token tok=tokenizer.lex(); + if (!tok.is(TokenRetval::TK_WHITESPACE)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\%s command", - qPrint(context.token->name)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", context.token->name); return; } tokenizer.setStatePrefix(); tok=tokenizer.lex(); - if (tok==0) + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) { warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the " - "argument of command %s",qPrint(context.token->name)); + "argument of command {}",context.token->name); return; } - else if (tok!=TK_WORD) + else if (!tok.is(TokenRetval::TK_WORD)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token %s as the argument of %s", - DocTokenizer::tokToString(tok),qPrint(context.token->name)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}", + tok.to_string(),context.token->name); return; } context.prefix = context.token->name; @@ -996,22 +1009,22 @@ void DocParser::handlePrefix(DocNodeVariant *parent,DocNodeList &children) * @param[out] width the extracted width specifier * @param[out] height the extracted height specifier */ -void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, DocNodeList &children, QCString &width,QCString &height) +void DocParser::defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, QCString &width,QCString &height) { AUTO_TRACE(); auto ns = AutoNodeStack(this,parent); // parse title tokenizer.setStateTitle(); - int tok = 0; - while ((tok=tokenizer.lex())) + Token tok = tokenizer.lex(); + while (!tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) { - if (tok==TK_WORD && (context.token->name=="width=" || context.token->name=="height=")) + if (tok.is(TokenRetval::TK_WORD) && (context.token->name=="width=" || context.token->name=="height=")) { // special case: no title, but we do have a size indicator break; } - else if (tok==TK_HTMLTAG) + else if (tok.is(TokenRetval::TK_HTMLTAG)) { tokenizer.unputString(context.token->text); break; @@ -1020,15 +1033,16 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, { errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd)); } + tok = tokenizer.lex(); } // parse size attributes - if (tok == 0) + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) { tok=tokenizer.lex(); } - while (tok==TK_WHITESPACE || tok==TK_WORD || tok==TK_HTMLTAG) // there are values following the title + while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) // there are values following the title { - if (tok==TK_WORD) + if (tok.is(TokenRetval::TK_WORD)) { if (context.token->name=="width=" || context.token->name=="height=") { @@ -1047,8 +1061,8 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, else // other text after the title -> treat as normal text { tokenizer.unputString(context.token->name); - //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '%s' after \\%s command, expected 'width' or 'height'", - // qPrint(context.token->name), qPrint(Mappers::cmdMapper->find(cmd))); + //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '{}' after \\{} command, expected 'width' or 'height'", + // context.token->name, Mappers::cmdMapper->find(cmd)); break; } } @@ -1056,18 +1070,18 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, tok=tokenizer.lex(); // if we found something we did not expect, push it back to the stream // so it can still be processed - if (tok==TK_COMMAND_AT || tok==TK_COMMAND_BS) + if (tok.is_any_of(TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS)) { tokenizer.unputString(context.token->name); - tokenizer.unputString(tok==TK_COMMAND_AT ? "@" : "\\"); + tokenizer.unputString(tok.is(TokenRetval::TK_COMMAND_AT) ? "@" : "\\"); break; } - else if (tok==TK_SYMBOL) + else if (tok.is(TokenRetval::TK_SYMBOL)) { tokenizer.unputString(context.token->name); break; } - else if (tok==TK_HTMLTAG) + else if (tok.is(TokenRetval::TK_HTMLTAG)) { tokenizer.unputString(context.token->text); break; @@ -1085,10 +1099,10 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) bool inlineImage = false; QCString anchorStr; - int tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + Token tok=tokenizer.lex(); + if (!tok.is(TokenRetval::TK_WHITESPACE)) { - if (tok==TK_WORD) + if (tok.is(TokenRetval::TK_WORD)) { if (context.token->name == "{") { @@ -1112,8 +1126,8 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) if (!anchorStr.isEmpty()) { warn_doc_error(context.fileName,tokenizer.getLineNr(), - "multiple use of option 'anchor' for 'image' command, ignoring: '%s'", - qPrint(locOpt.mid(7))); + "multiple use of option 'anchor' for 'image' command, ignoring: '{}'", + locOpt.mid(7)); } else { @@ -1123,12 +1137,12 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) else { warn_doc_error(context.fileName,tokenizer.getLineNr(), - "unknown option '%s' for 'image' command specified", - qPrint(locOpt)); + "unknown option '{}' for 'image' command specified", + locOpt); } } tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + if (!tok.is(TokenRetval::TK_WHITESPACE)) { warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command"); return; @@ -1142,14 +1156,14 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) } } tok=tokenizer.lex(); - if (tok!=TK_WORD && tok!=TK_LNKWORD) + if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token %s as the argument of \\image", - DocTokenizer::tokToString(tok)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", + tok.to_string()); return; } tok=tokenizer.lex(); - if (tok!=TK_WHITESPACE) + if (!tok.is(TokenRetval::TK_WHITESPACE)) { warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command"); return; @@ -1163,18 +1177,16 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) else if (imgType=="xml") t=DocImage::Xml; else { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `%s` specified as the first argument of " - "\\image command is not valid", - qPrint(imgType)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `{}` specified as the first argument of " + "\\image command is not valid", imgType); return; } tokenizer.setStateFile(); tok=tokenizer.lex(); tokenizer.setStatePara(); - if (tok!=TK_WORD) + if (!tok.is(TokenRetval::TK_WORD)) { - warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token %s as the argument of \\image", - DocTokenizer::tokToString(tok)); + warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", tok.to_string()); return; } if (!anchorStr.isEmpty()) @@ -1199,194 +1211,218 @@ void DocParser::handleImage(DocNodeVariant *parent, DocNodeList &children) * @retval TRUE The token was handled. * @retval FALSE The token was not handled. */ -bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &children,bool handleWord) +bool DocParser::defaultHandleToken(DocNodeVariant *parent,Token tok, DocNodeList &children,bool handleWord) { - AUTO_TRACE("token={} handleWord={}",DocTokenizer::tokToString(tok),handleWord); - if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || - tok==TK_COMMAND_AT || tok==TK_COMMAND_BS || tok==TK_HTMLTAG + AUTO_TRACE("token={} handleWord={}",tok.to_string(),handleWord); + if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL, + TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG) ) { } reparsetoken: QCString tokenName = context.token->name; AUTO_TRACE_ADD("tokenName={}",tokenName); - switch (tok) + switch (tok.value()) { - case TK_COMMAND_AT: + case TokenRetval::TK_COMMAND_AT: // fall through - case TK_COMMAND_BS: + case TokenRetval::TK_COMMAND_BS: switch (Mappers::cmdMapper->map(tokenName)) { - case CMD_BSLASH: + case CommandType::CMD_BSLASH: children.append(this,parent,HtmlEntityMapper::Sym_BSlash); break; - case CMD_AT: + case CommandType::CMD_AT: children.append(this,parent,HtmlEntityMapper::Sym_At); break; - case CMD_LESS: + case CommandType::CMD_LESS: children.append(this,parent,HtmlEntityMapper::Sym_Less); break; - case CMD_GREATER: + case CommandType::CMD_GREATER: children.append(this,parent,HtmlEntityMapper::Sym_Greater); break; - case CMD_AMP: + case CommandType::CMD_AMP: children.append(this,parent,HtmlEntityMapper::Sym_Amp); break; - case CMD_DOLLAR: + case CommandType::CMD_DOLLAR: children.append(this,parent,HtmlEntityMapper::Sym_Dollar); break; - case CMD_HASH: + case CommandType::CMD_HASH: children.append(this,parent,HtmlEntityMapper::Sym_Hash); break; - case CMD_DCOLON: + case CommandType::CMD_DCOLON: children.append(this,parent,HtmlEntityMapper::Sym_DoubleColon); break; - case CMD_PERCENT: + case CommandType::CMD_PERCENT: children.append(this,parent,HtmlEntityMapper::Sym_Percent); break; - case CMD_NDASH: + case CommandType::CMD_NDASH: children.append(this,parent,HtmlEntityMapper::Sym_Minus); children.append(this,parent,HtmlEntityMapper::Sym_Minus); break; - case CMD_MDASH: + case CommandType::CMD_MDASH: children.append(this,parent,HtmlEntityMapper::Sym_Minus); children.append(this,parent,HtmlEntityMapper::Sym_Minus); children.append(this,parent,HtmlEntityMapper::Sym_Minus); break; - case CMD_QUOTE: + case CommandType::CMD_QUOTE: children.append(this,parent,HtmlEntityMapper::Sym_Quot); break; - case CMD_PUNT: + case CommandType::CMD_PUNT: children.append(this,parent,HtmlEntityMapper::Sym_Dot); break; - case CMD_PLUS: + case CommandType::CMD_EXCLAMATION: + children.append(this,parent,HtmlEntityMapper::Sym_Exclam); + break; + case CommandType::CMD_QUESTION: + children.append(this,parent,HtmlEntityMapper::Sym_Quest); + break; + case CommandType::CMD_PLUS: children.append(this,parent,HtmlEntityMapper::Sym_Plus); break; - case CMD_MINUS: + case CommandType::CMD_MINUS: children.append(this,parent,HtmlEntityMapper::Sym_Minus); break; - case CMD_EQUAL: + case CommandType::CMD_EQUAL: children.append(this,parent,HtmlEntityMapper::Sym_Equal); break; - case CMD_EMPHASIS: + case CommandType::CMD_EMPHASIS: { children.append(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,TRUE); tok=handleStyleArgument(parent,children,tokenName); children.append(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,FALSE); - if (tok!=TK_WORD) children.append(this,parent," "); - if (tok==TK_NEWPARA) goto handlepara; - else if (tok==TK_WORD || tok==TK_HTMLTAG) + if (!tok.is(TokenRetval::TK_WORD)) children.append(this,parent," "); + if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara; + else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) { - AUTO_TRACE_ADD("CMD_EMPHASIS: reparsing"); + AUTO_TRACE_ADD("CommandType::CMD_EMPHASIS: reparsing"); goto reparsetoken; } } break; - case CMD_BOLD: + case CommandType::CMD_BOLD: { children.append(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,TRUE); tok=handleStyleArgument(parent,children,tokenName); children.append(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,FALSE); - if (tok!=TK_WORD) children.append(this,parent," "); - if (tok==TK_NEWPARA) goto handlepara; - else if (tok==TK_WORD || tok==TK_HTMLTAG) + if (!tok.is(TokenRetval::TK_WORD)) children.append(this,parent," "); + if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara; + else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) { - AUTO_TRACE_ADD("CMD_BOLD: reparsing"); + AUTO_TRACE_ADD("CommandType::CMD_BOLD: reparsing"); goto reparsetoken; } } break; - case CMD_CODE: + case CommandType::CMD_CODE: { children.append(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,TRUE); tok=handleStyleArgument(parent,children,tokenName); children.append(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,FALSE); - if (tok!=TK_WORD) children.append(this,parent," "); - if (tok==TK_NEWPARA) goto handlepara; - else if (tok==TK_WORD || tok==TK_HTMLTAG) + if (!tok.is(TokenRetval::TK_WORD)) children.append(this,parent," "); + if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara; + else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) { - AUTO_TRACE_ADD("CMD_CODE: reparsing"); + AUTO_TRACE_ADD("CommandType::CMD_CODE: reparsing"); goto reparsetoken; } } break; - case CMD_HTMLONLY: + case CommandType::CMD_HTMLONLY: { tokenizer.setStateHtmlOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::HtmlOnly,context.isExample,context.exampleName,context.token->name=="block"); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_MANONLY: + case CommandType::CMD_MANONLY: { tokenizer.setStateManOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::ManOnly,context.isExample,context.exampleName); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_RTFONLY: + case CommandType::CMD_RTFONLY: { tokenizer.setStateRtfOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::RtfOnly,context.isExample,context.exampleName); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_LATEXONLY: + case CommandType::CMD_LATEXONLY: { tokenizer.setStateLatexOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::LatexOnly,context.isExample,context.exampleName); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_XMLONLY: + case CommandType::CMD_XMLONLY: { tokenizer.setStateXmlOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::XmlOnly,context.isExample,context.exampleName); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_DBONLY: + case CommandType::CMD_DBONLY: { tokenizer.setStateDbOnly(); tok = tokenizer.lex(); children.append(this,parent,context.context,context.token->verb,DocVerbatim::DocbookOnly,context.isExample,context.exampleName); - if (tok==0) warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker"); + if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) + { + warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker"); + } tokenizer.setStatePara(); } break; - case CMD_FORMULA: + case CommandType::CMD_FORMULA: { children.append(this,parent,context.token->id); } break; - case CMD_ANCHOR: - case CMD_IANCHOR: + case CommandType::CMD_ANCHOR: + case CommandType::CMD_IANCHOR: { handleAnchor(parent,children); } break; - case CMD_IPREFIX: + case CommandType::CMD_IPREFIX: { handlePrefix(parent,children); } break; - case CMD_INTERNALREF: + case CommandType::CMD_INTERNALREF: { handleInternalRef(parent,children); tokenizer.setStatePara(); } break; - case CMD_SETSCOPE: + case CommandType::CMD_SETSCOPE: { tokenizer.setStateSetScope(); (void)tokenizer.lex(); @@ -1395,157 +1431,99 @@ bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList & tokenizer.setStatePara(); } break; - case CMD_IMAGE: + case CommandType::CMD_IMAGE: handleImage(parent,children); break; + case CommandType::CMD_ILINE: + tokenizer.pushState(); + tokenizer.setStateILine(); + (void)tokenizer.lex(); + tokenizer.popState(); + break; + case CommandType::CMD_IFILE: + tokenizer.pushState(); + tokenizer.setStateIFile(); + (void)tokenizer.lex(); + tokenizer.popState(); + break; default: return FALSE; } break; - case TK_HTMLTAG: + case TokenRetval::TK_HTMLTAG: { - switch (Mappers::htmlTagMapper->map(tokenName)) - { - case HTML_DIV: - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found
      tag in heading"); - break; - case HTML_PRE: - warn_doc_error(context.fileName,tokenizer.getLineNr(),"found
       tag in heading");
      -            break;
      -          case HTML_BOLD:
      +        auto handleEnterLeaveStyle = [this,&parent,&children,&tokenName](DocStyleChange::Style style) {
                   if (!context.token->endTag)
                   {
      -              handleStyleEnter(parent,children,DocStyleChange::Bold,tokenName,&context.token->attribs);
      +              handleStyleEnter(parent,children,style,tokenName,&context.token->attribs);
                   }
                   else
                   {
      -              handleStyleLeave(parent,children,DocStyleChange::Bold,tokenName);
      +              handleStyleLeave(parent,children,style,tokenName);
                   }
      +        };
      +        switch (Mappers::htmlTagMapper->map(tokenName))
      +        {
      +          case HtmlTagType::HTML_DIV:
      +            warn_doc_error(context.fileName,tokenizer.getLineNr(),"found 
      tag in heading"); break; - case HTML_S: - if (!context.token->endTag) - { - handleStyleEnter(parent,children,DocStyleChange::S,tokenName,&context.token->attribs); - } - else - { - handleStyleLeave(parent,children,DocStyleChange::S,tokenName); - } + case HtmlTagType::HTML_PRE: + warn_doc_error(context.fileName,tokenizer.getLineNr(),"found
       tag in heading");
                   break;
      -          case HTML_STRIKE:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Strike,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Strike,tokenName);
      -            }
      +          case HtmlTagType::HTML_SPAN:
      +            handleEnterLeaveStyle(DocStyleChange::Span);
                   break;
      -          case HTML_DEL:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Del,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Del,tokenName);
      -            }
      +          case HtmlTagType::HTML_BOLD:
      +            handleEnterLeaveStyle(DocStyleChange::Bold);
                   break;
      -          case HTML_UNDERLINE:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Underline,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Underline,tokenName);
      -            }
      +          case HtmlTagType::HTML_S:
      +            handleEnterLeaveStyle(DocStyleChange::S);
                   break;
      -          case HTML_INS:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Ins,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Ins,tokenName);
      -            }
      +          case HtmlTagType::HTML_STRIKE:
      +            handleEnterLeaveStyle(DocStyleChange::Strike);
                   break;
      -          case HTML_CODE:
      -          case XML_C:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Code,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Code,tokenName);
      -            }
      +          case HtmlTagType::HTML_DEL:
      +            handleEnterLeaveStyle(DocStyleChange::Del);
                   break;
      -          case HTML_EMPHASIS:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Italic,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Italic,tokenName);
      -            }
      +          case HtmlTagType::HTML_UNDERLINE:
      +            handleEnterLeaveStyle(DocStyleChange::Underline);
                   break;
      -          case HTML_SUB:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Subscript,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Subscript,tokenName);
      -            }
      +          case HtmlTagType::HTML_INS:
      +            handleEnterLeaveStyle(DocStyleChange::Ins);
                   break;
      -          case HTML_SUP:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Superscript,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Superscript,tokenName);
      -            }
      +          case HtmlTagType::HTML_CODE:
      +          case HtmlTagType::XML_C:
      +            handleEnterLeaveStyle(DocStyleChange::Code);
                   break;
      -          case HTML_CENTER:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Center,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Center,tokenName);
      -            }
      +          case HtmlTagType::HTML_KBD:
      +            handleEnterLeaveStyle(DocStyleChange::Kbd);
                   break;
      -          case HTML_SMALL:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Small,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Small,tokenName);
      -            }
      +          case HtmlTagType::HTML_TT:
      +            handleEnterLeaveStyle(DocStyleChange::Typewriter);
                   break;
      -          case HTML_CITE:
      -            if (!context.token->endTag)
      -            {
      -              handleStyleEnter(parent,children,DocStyleChange::Cite,tokenName,&context.token->attribs);
      -            }
      -            else
      -            {
      -              handleStyleLeave(parent,children,DocStyleChange::Cite,tokenName);
      -            }
      +          case HtmlTagType::HTML_EMPHASIS:
      +            handleEnterLeaveStyle(DocStyleChange::Italic);
      +            break;
      +          case HtmlTagType::HTML_SUB:
      +            handleEnterLeaveStyle(DocStyleChange::Subscript);
                   break;
      -          case HTML_IMG:
      +          case HtmlTagType::HTML_SUP:
      +            handleEnterLeaveStyle(DocStyleChange::Superscript);
      +            break;
      +          case HtmlTagType::HTML_CENTER:
      +            handleEnterLeaveStyle(DocStyleChange::Center);
      +            break;
      +          case HtmlTagType::HTML_SMALL:
      +            handleEnterLeaveStyle(DocStyleChange::Small);
      +            break;
      +          case HtmlTagType::HTML_CITE:
      +            handleEnterLeaveStyle(DocStyleChange::Cite);
      +            break;
      +          case HtmlTagType::HTML_IMG:
                   if (!context.token->endTag)
      +            {
                     handleImg(parent,children,context.token->attribs);
      +            }
       	    break;
                 default:
                   return FALSE;
      @@ -1553,7 +1531,7 @@ bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &
               }
             }
             break;
      -    case TK_SYMBOL:
      +    case TokenRetval::TK_SYMBOL:
             {
               HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(tokenName);
               if (s!=HtmlEntityMapper::Sym_Unknown)
      @@ -1566,15 +1544,15 @@ bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &
               }
             }
             break;
      -    case TK_WHITESPACE:
      -    case TK_NEWPARA:
      +    case TokenRetval::TK_WHITESPACE:
      +    case TokenRetval::TK_NEWPARA:
       handlepara:
             if (insidePRE(parent) || !children.empty())
             {
               children.append(this,parent,context.token->chars);
             }
             break;
      -    case TK_LNKWORD:
      +    case TokenRetval::TK_LNKWORD:
             if (handleWord)
             {
               handleLinkedWord(parent,children);
      @@ -1582,7 +1560,7 @@ bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &
             else
               return FALSE;
             break;
      -    case TK_WORD:
      +    case TokenRetval::TK_WORD:
             if (handleWord)
             {
               children.append(this,parent,context.token->name);
      @@ -1590,7 +1568,7 @@ bool DocParser::defaultHandleToken(DocNodeVariant *parent,int tok, DocNodeList &
             else
               return FALSE;
             break;
      -    case TK_URL:
      +    case TokenRetval::TK_URL:
             if (context.insideHtmlLink)
             {
               children.append(this,parent,context.token->name);
      @@ -1639,11 +1617,11 @@ void DocParser::handleImg(DocNodeVariant *parent, DocNodeList &children,const Ht
       
       //---------------------------------------------------------------------------
       
      -int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &children,
      +Token DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &children,
                                           const QCString &doc)
       {
         AUTO_TRACE();
      -  int retval = RetVal_OK;
      +  Token retval = Token::make_RetVal_OK();
       
         if (doc.isEmpty()) return retval;
       
      @@ -1671,7 +1649,7 @@ int DocParser::internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &ch
           {
             children.pop_back();
           }
      -  } while (retval==TK_NEWPARA);
      +  } while (retval.is(TokenRetval::TK_NEWPARA));
         if (lastPar) lastPar->markLast();
       
         AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
      @@ -1690,16 +1668,14 @@ void DocParser::readTextFileByName(const QCString &file,QCString &text)
           text = fileToString(filePath,Config_getBool(FILTER_SOURCE_FILES));
           if (ambig)
           {
      -      warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '%s' is ambiguous"
      -          "Possible candidates:\n%s",qPrint(file),
      -          qPrint(showFileDefMatches(Doxygen::exampleNameLinkedMap,file))
      -          );
      +      warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '{}' is ambiguous"
      +          "Possible candidates:\n{}",file, showFileDefMatches(Doxygen::exampleNameLinkedMap,file));
           }
         }
         else
         {
      -    warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '%s' is not found. "
      -           "Check your EXAMPLE_PATH",qPrint(file));
      +    warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '{}' is not found. "
      +           "Check your EXAMPLE_PATH",file);
         }
       }
       
      @@ -1724,11 +1700,13 @@ static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
               case '\'': insideSQuote=TRUE; break;
               case '\\': // fall through, begin of command
               case '@':  // fall through, begin of command
      -        case ' ':  // fall through
               case '\t': // fall through
               case '\n':
                 found=(round==0);
                 break;
      +        case ' ':  // allow spaces in cast operator (see issue #11169)
      +          found=(round==0) && (j<8 || !literal_at(data+j-8,"operator"));
      +          break;
             }
           }
           else if (insideSQuote) // look for single quote end
      @@ -1747,16 +1725,26 @@ static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
           }
           if (!found) j++;
         }
      -  if (qstrncmp(data+j," const",6)==0)
      +
      +  // include const and volatile
      +  if (literal_at(data+j," const"))
         {
           j+=6;
         }
      -  else if (qstrncmp(data+j," volatile",9)==0)
      +  else if (literal_at(data+j," volatile"))
         {
           j+=9;
         }
      +
      +  // allow '&' or '&&' or ' &' or ' &&' at the end
      +  size_t k=j;
      +  while (k0 && data[j-1]=='.') { e--; } // do not include punctuation added by Definition::_setBriefDescription()
      +  if (j>0 && data[j-1]=='.') { e--; }
         QCString id(data+s,e-s);
         //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
         return id;
      @@ -1767,7 +1755,7 @@ static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
       // and the sizeof(str) returns the size of str including the '\0' terminator;
       // a fact we abuse to skip over the start of the command character.
       #define CHECK_FOR_COMMAND(str,action) \
      -   do if ((i+sizeof(str)isReference())
               {
                 warn_doc_error(context.fileName,tokenizer.getLineNr(),
      -               "@copy%s or @copydoc target '%s' found but is from a tag file, skipped", isBrief?"brief":"details",
      -               qPrint(id));
      +               "@copy{} or @copydoc target '{}' found but is from a tag file, skipped",
      +               isBrief?"brief":"details", id);
               }
               else if (found)
               {
      @@ -1863,10 +1852,10 @@ QCString DocParser::processCopyDoc(const char *data,size_t &len)
                   context.copyStack.push_back(def);
                   auto addDocs = [&](const QCString &file_,int line_,const QCString &doc_)
                   {
      -              buf.addStr(" \\ifile \""+file_+"\" ");
      -              buf.addStr("\\iline "+QCString().setNum(line_)+" \\ilinebr ");
      +              result+=" \\ifile \""+file_+"\" ";
      +              result+="\\iline "+QCString().setNum(line_)+" \\ilinebr ";
                     size_t len_ = doc_.length();
      -              buf.addStr(processCopyDoc(doc_.data(),len_));
      +              result+=processCopyDoc(doc_.data(),len_);
                   };
                   if (isBrief)
                   {
      @@ -1881,25 +1870,24 @@ QCString DocParser::processCopyDoc(const char *data,size_t &len)
                       const ArgumentList &docArgList = md->templateMaster() ?
                           md->templateMaster()->argumentList() :
                           md->argumentList();
      -                buf.addStr(inlineArgListToDoc(docArgList));
      +                result+=inlineArgListToDoc(docArgList);
                     }
                   }
                   context.copyStack.pop_back();
      -            buf.addStr(" \\ilinebr \\ifile \""+context.fileName+"\" ");
      -            buf.addStr("\\iline "+QCString().setNum(lineNr)+" ");
      +            result+=" \\ilinebr \\ifile \""+context.fileName+"\" ";
      +            result+="\\iline "+QCString().setNum(lineNr)+" ";
                 }
                 else
                 {
                   warn_doc_error(context.fileName,tokenizer.getLineNr(),
      -	         "Found recursive @copy%s or @copydoc relation for argument '%s'.",
      -                 isBrief?"brief":"details",qPrint(id));
      +	         "Found recursive @copy{} or @copydoc relation for argument '{}'.",
      +                 isBrief?"brief":"details",id);
                 }
               }
               else
               {
                 warn_doc_error(context.fileName,tokenizer.getLineNr(),
      -               "@copy%s or @copydoc target '%s' not found", isBrief?"brief":"details",
      -               qPrint(id));
      +               "@copy{} or @copydoc target '{}' not found", isBrief?"brief":"details",id);
               }
               // skip over command
               i=j;
      @@ -1912,46 +1900,45 @@ QCString DocParser::processCopyDoc(const char *data,size_t &len)
               {
                 size_t orgPos = i;
                 i=skipToEndMarker(data,k,len,endMarker);
      -          buf.addStr(data+orgPos,i-orgPos);
      +          result+=QCString(data+orgPos,i-orgPos);
                 // TODO: adjust lineNr
               }
               else
               {
      -          buf.addChar(c);
      +          result+=c;
                 i++;
               }
             }
           }
           else // not a command, just copy
           {
      -      buf.addChar(c);
      +      result+=c;
             i++;
             lineNr += (c=='\n') ? 1 : 0;
           }
         }
      -  len = buf.getPos();
      -  buf.addChar(0);
      -  AUTO_TRACE_EXIT("result={}",Trace::trunc(buf.get()));
      -  return buf.get();
      +  len = result.length();
      +  AUTO_TRACE_EXIT("result={}",Trace::trunc(result));
      +  return result;
       }
       
       
       //---------------------------------------------------------------------------
       
       IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
      -                            const QCString &fileName,int startLine,
      -                            const Definition *ctx,const MemberDef *md,
      -                            const QCString &input,bool indexWords,
      -                            bool isExample, const QCString &exampleName,
      -                            bool singleLine, bool linkFromIndex,
      -                            bool markdownSupport)
      +                                  const QCString &fileName,
      +                                  int startLine,
      +                                  const Definition *ctx,
      +                                  const MemberDef *md,
      +                                  const QCString &input,
      +                                  const DocOptions &options)
       {
         DocParser *parser = dynamic_cast(&parserIntf);
         assert(parser!=nullptr);
         if (parser==nullptr) return nullptr;
         //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?qPrint(ctx->name()):"",
         //                                     md?qPrint(md->name()):"",
      -  //                                     input);
      +  //                                     qPrint(input));
         //printf("========== validating %s at line %d\n",qPrint(fileName),startLine);
         //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",qPrint(input));
       
      @@ -1989,7 +1976,7 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
         parser->context.scope = ctx;
         parser->context.lang = getLanguageFromFileName(fileName);
       
      -  if (indexWords && Doxygen::searchIndex.enabled())
      +  if (options.indexWords() && Doxygen::searchIndex.enabled())
         {
           if (md)
           {
      @@ -2008,8 +1995,8 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
         }
       
         parser->context.fileName = fileName;
      -  parser->context.relPath = (!linkFromIndex && ctx) ?
      -               QCString(relativePathToRoot(ctx->getOutputFileBase())) :
      +  parser->context.relPath = (!options.linkFromIndex() && ctx) ?
      +               relativePathToRoot(ctx->getOutputFileBase()) :
                      QCString("");
         //printf("ctx->name=%s relPath=%s\n",qPrint(ctx->name()),qPrint(parser->context.relPath));
         parser->context.memberDef = md;
      @@ -2017,18 +2004,20 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
         while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
         while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
         parser->context.inSeeBlock = FALSE;
      +  parser->context.inCodeStyle = FALSE;
         parser->context.xmlComment = FALSE;
         parser->context.insideHtmlLink = FALSE;
         parser->context.includeFileText = "";
         parser->context.includeFileOffset = 0;
         parser->context.includeFileLength = 0;
      -  parser->context.isExample = isExample;
      -  parser->context.exampleName = exampleName;
      +  parser->context.isExample = options.isExample();
      +  parser->context.exampleName = options.exampleName();
         parser->context.hasParamCommand = FALSE;
         parser->context.hasReturnCommand = FALSE;
         parser->context.retvalsFound.clear();
         parser->context.paramsFound.clear();
      -  parser->context.markdownSupport = markdownSupport;
      +  parser->context.markdownSupport = options.markdownSupport();
      +  parser->context.autolinkSupport = options.autolinkSupport();
       
         //printf("Starting comment block at %s:%d\n",qPrint(parser->context.fileName),startLine);
         parser->tokenizer.setLineNr(startLine);
      @@ -2038,12 +2027,12 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
         {
           inpStr+='\n';
         }
      -  //printf("processCopyDoc(in='%s' out='%s')\n",input,qPrint(inpStr));
      +  //printf("processCopyDoc(in='%s' out='%s')\n",qPrint(input),qPrint(inpStr));
         parser->tokenizer.init(inpStr.data(),parser->context.fileName,
                                parser->context.markdownSupport,parser->context.insideHtmlLink);
       
         // build abstract syntax tree
      -  auto ast = std::make_unique(DocRoot(parser,md!=nullptr,singleLine));
      +  auto ast = std::make_unique(DocRoot(parser,md!=nullptr,options.singleLine()));
         std::get(ast->root).parse();
       
         if (Debug::isFlagSet(Debug::PrintTree))
      @@ -2067,6 +2056,59 @@ IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,
         return ast;
       }
       
      +IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf,const QCString &fileName,int lineNr,const QCString &input)
      +{
      +  DocParser *parser = dynamic_cast(&parserIntf);
      +  assert(parser!=nullptr);
      +  if (parser==nullptr) return nullptr;
      +
      +  // set initial token
      +  parser->context.token = parser->tokenizer.resetToken();
      +
      +  //printf("------------ input ---------\n%s\n"
      +  //       "------------ end input -----\n",input);
      +  parser->context.context = "";
      +  parser->context.fileName = fileName;
      +  parser->context.relPath = "";
      +  parser->context.memberDef = nullptr;
      +  while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
      +  while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
      +  while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
      +  parser->context.inSeeBlock = FALSE;
      +  parser->context.inCodeStyle = FALSE;
      +  parser->context.xmlComment = FALSE;
      +  parser->context.insideHtmlLink = FALSE;
      +  parser->context.includeFileText = "";
      +  parser->context.includeFileOffset = 0;
      +  parser->context.includeFileLength = 0;
      +  parser->context.isExample = FALSE;
      +  parser->context.exampleName = "";
      +  parser->context.hasParamCommand = FALSE;
      +  parser->context.hasReturnCommand = FALSE;
      +  parser->context.retvalsFound.clear();
      +  parser->context.paramsFound.clear();
      +  parser->context.searchUrl="";
      +  parser->context.lang = SrcLangExt::Unknown;
      +  parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
      +  parser->context.autolinkSupport = false;
      +
      +  auto ast = std::make_unique(DocTitle(parser,nullptr));
      +
      +  if (!input.isEmpty())
      +  {
      +    // build abstract syntax tree from title string
      +    std::get(ast->root).parseFromString(nullptr,input);
      +
      +    if (Debug::isFlagSet(Debug::PrintTree))
      +    {
      +      // pretty print the result
      +      std::visit(PrintDocVisitor{},ast->root);
      +    }
      +  }
      +
      +  return ast;
      +}
      +
       IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
       {
         DocParser *parser = dynamic_cast(&parserIntf);
      @@ -2087,6 +2129,7 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
         while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
         while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
         parser->context.inSeeBlock = FALSE;
      +  parser->context.inCodeStyle = FALSE;
         parser->context.xmlComment = FALSE;
         parser->context.insideHtmlLink = FALSE;
         parser->context.includeFileText = "";
      @@ -2101,6 +2144,7 @@ IDocNodeASTPtr validatingParseText(IDocParser &parserIntf,const QCString &input)
         parser->context.searchUrl="";
         parser->context.lang = SrcLangExt::Unknown;
         parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
      +  parser->context.autolinkSupport = FALSE;
       
       
         auto ast = std::make_unique(DocText(parser));
      diff --git a/src/docparser.h b/src/docparser.h
      index deddbc70f7d..ae3f46ee19f 100644
      --- a/src/docparser.h
      +++ b/src/docparser.h
      @@ -23,6 +23,7 @@
       #include "growvector.h"
       #include "construct.h"
       #include "types.h"
      +#include "docoptions.h"
       
       class MemberDef;
       class Definition;
      @@ -55,6 +56,7 @@ class IDocNodeAST
       
       using IDocNodeASTPtr = std::unique_ptr;
       
      +
       /*! Main entry point for the comment block parser.
        *  @param parserIntf The parser object created via createDocParser()
        *  @param fileName  File in which the documentation block is found (or the
      @@ -64,32 +66,30 @@ using IDocNodeASTPtr = std::unique_ptr;
        *  @param md        Member definition to which the documentation belongs.
        *                   Can be 0.
        *  @param input     String representation of the documentation block.
      - *  @param indexWords Indicates whether or not words should be put in the
      - *                   search index.
      - *  @param isExample TRUE if the documentation belongs to an example.
      - *  @param exampleName Base name of the example file (0 if isExample is FALSE).
      - *  @param singleLine Output should be presented on a single line, so without
      - *                   starting a new paragraph at the end.
      - *  @param linkFromIndex TRUE if the documentation is generated from an
      - *                   index page. In this case context is not used to determine
      - *                   the relative path when making a link.
      - *  @param markdownSupport TRUE if the input needs to take markdown markup into
      - *                   account.
      + *  @param options   Optional parameters.
        *  @returns         An object representing the abstract syntax tree. Ownership of the
        *                   pointer is handed over to the caller.
        */
      -IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf,const QCString &fileName,int startLine,
      -                            const Definition *ctx, const MemberDef *md,
      -                            const QCString &input,bool indexWords,
      -                            bool isExample,const QCString &exampleName,
      -                            bool singleLine,bool linkFromIndex,
      -                            bool markdownSupport);
      +IDocNodeASTPtr validatingParseDoc(
      +    IDocParser &parserIntf,
      +    const QCString &fileName,
      +    int startLine,
      +    const Definition *ctx,
      +    const MemberDef *md,
      +    const QCString &input,
      +    const DocOptions &options);
       
       /*! Main entry point for parsing simple text fragments. These
        *  fragments are limited to words, whitespace and symbols.
        */
       IDocNodeASTPtr validatingParseText(IDocParser &parser,const QCString &input);
       
      +
      +/*! Main entry point for parsing titles. These allow limited markup commands */
      +IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf,const QCString &fileName,int lineNr,
      +                                    const QCString &input);
      +
      +
       IDocNodeASTPtr createRef(IDocParser &parser,const QCString &target,const QCString &context, const QCString &srcFile = "", int srcLine = -1);
       
       //--------------------------------------------------------------------------------
      diff --git a/src/docparser_p.h b/src/docparser_p.h
      index ba6c2de13f5..48894fef53f 100644
      --- a/src/docparser_p.h
      +++ b/src/docparser_p.h
      @@ -33,6 +33,7 @@
       #include "doctokenizer.h"
       #include "searchindex.h"
       #include "construct.h"
      +#include "cmdmapper.h"
       
       using DefinitionStack = std::vector;
       using DocNodeStack = std::stack;
      @@ -60,6 +61,7 @@ struct DocParserContext
         const Definition *scope = nullptr;
         QCString context;
         bool inSeeBlock = false;
      +  bool inCodeStyle = false;
         bool xmlComment = false;
         bool insideHtmlLink = false;
         DocNodeStack nodeStack;
      @@ -86,10 +88,12 @@ struct DocParserContext
         size_t       includeFileLength = 0;
         int          includeFileLine;
         bool         includeFileShowLineNo = false;
      +  bool         stripCodeComments = true;
       
         TokenInfo *token = nullptr;
         int      lineNo = 0;
         bool     markdownSupport = false;
      +  bool     autolinkSupport = false;
       };
       
       class DocParser : public IDocParser
      @@ -98,8 +102,8 @@ class DocParser : public IDocParser
           void pushContext();
           void popContext();
           void handleImg(DocNodeVariant *parent,DocNodeList &children,const HtmlAttribList &tagHtmlAttribs);
      -    int  internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &children,
      -                                    const QCString &doc);
      +    Token internalValidatingParseDoc(DocNodeVariant *parent,DocNodeList &children,
      +                                      const QCString &doc);
           QCString processCopyDoc(const char *data,size_t &len);
           QCString findAndCopyImage(const QCString &fileName,DocImage::Type type, bool doWarn = true);
           void checkArgumentName();
      @@ -109,22 +113,22 @@ class DocParser : public IDocParser
                                            QCString *pDoc,
                                            QCString *pBrief,
                                            const Definition **pDef);
      -    bool defaultHandleToken(DocNodeVariant *parent,int tok,
      +    bool defaultHandleToken(DocNodeVariant *parent,Token tok,
                                   DocNodeList &children,bool
                                   handleWord=TRUE);
      -    void errorHandleDefaultToken(DocNodeVariant *parent,int tok,
      +    void errorHandleDefaultToken(DocNodeVariant *parent,Token tok,
                                        DocNodeList &children,const QCString &txt);
      -    void defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent,
      +    void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent,
                                          DocNodeList &children, QCString &width,QCString &height);
      -    int handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
      -                            const QCString &cmdName);
      +    Token handleStyleArgument(DocNodeVariant *parent,DocNodeList &children,
      +                               const QCString &cmdName);
           void handleStyleEnter(DocNodeVariant *parent,DocNodeList &children, DocStyleChange::Style s,
                                 const QCString &tagName,const HtmlAttribList *attribs);
           void handleStyleLeave(DocNodeVariant *parent,DocNodeList &children, DocStyleChange::Style s,
                                 const QCString &tagName);
           void handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &children);
           void handleInitialStyleCommands(DocNodeVariant *parent,DocNodeList &children);
      -    int  handleAHref(DocNodeVariant *parent,DocNodeList &children,const HtmlAttribList &tagHtmlAttribs);
      +    Token handleAHref(DocNodeVariant *parent,DocNodeList &children,const HtmlAttribList &tagHtmlAttribs);
           void handleUnclosedStyleCommands();
           void handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag=FALSE);
           void handleParameterType(DocNodeVariant *parent,DocNodeList &children,const QCString ¶mTypes);
      diff --git a/src/docsets.cpp b/src/docsets.cpp
      index b8f026c81fa..94bca7923c5 100644
      --- a/src/docsets.cpp
      +++ b/src/docsets.cpp
      @@ -68,7 +68,7 @@ void DocSets::initialize()
           std::ofstream ts = Portable::openOutputStream(mfName);
           if (!ts.is_open())
           {
      -      term("Could not open file %s for writing\n",qPrint(mfName));
      +      term("Could not open file {} for writing\n",mfName);
           }
       
           ts << "DOCSET_NAME=" << bundleId << ".docset\n"
      @@ -117,7 +117,7 @@ void DocSets::initialize()
           std::ofstream ts = Portable::openOutputStream(plName);
           if (!ts.is_open())
           {
      -      term("Could not open file %s for writing\n",qPrint(plName));
      +      term("Could not open file {} for writing\n",plName);
           }
       
           ts << "\n"
      @@ -153,7 +153,7 @@ void DocSets::initialize()
         p->ntf = Portable::openOutputStream(notes);
         if (!p->ntf.is_open())
         {
      -    term("Could not open file %s for writing\n",qPrint(notes));
      +    term("Could not open file {} for writing\n",notes);
         }
         p->nts.setStream(&p->ntf);
         //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
      @@ -171,7 +171,7 @@ void DocSets::initialize()
         p->ttf = Portable::openOutputStream(tokens);
         if (!p->ttf.is_open())
         {
      -    term("Could not open file %s for writing\n",qPrint(tokens));
      +    term("Could not open file {} for writing\n",tokens);
         }
         p->tts.setStream(&p->ttf);
         p->tts << "\n";
      @@ -229,7 +229,8 @@ void DocSets::addContentsItem(bool isDir,
                                     const QCString &anchor,
                                     bool /* separateIndex */,
                                     bool /* addToNavIndex */,
      -                              const Definition * /*def*/)
      +                              const Definition * /*def*/,
      +                              const QCString & /* nameAsHtml */)
       {
         (void)isDir;
         //printf("DocSets::addContentsItem(%s) depth=%zu\n",name,p->indentStack.size());
      @@ -333,22 +334,13 @@ void DocSets::addIndexItem(const Definition *context,const MemberDef *md,
           case SrcLangExt::Unknown: lang="unknown"; break;    // should not happen!
         }
       
      -  if (md)
      +  if (context && md)
         {
      -    if (context==nullptr)
      -    {
      -      if (md->getGroupDef())
      -        context = md->getGroupDef();
      -      else if (md->getFileDef())
      -        context = md->getFileDef();
      -    }
      -    if (context==nullptr) return; // should not happen
      -
           switch (md->memberType())
           {
      -      case MemberType_Define:
      +      case MemberType::Define:
               type="macro"; break;
      -      case MemberType_Function:
      +      case MemberType::Function:
               if (cd && (cd->compoundType()==ClassDef::Interface ||
                     cd->compoundType()==ClassDef::Class))
               {
      @@ -367,51 +359,42 @@ void DocSets::addIndexItem(const Definition *context,const MemberDef *md,
               else
                 type="func";
               break;
      -      case MemberType_Variable:
      +      case MemberType::Variable:
               type="data"; break;
      -      case MemberType_Typedef:
      +      case MemberType::Typedef:
               type="tdef"; break;
      -      case MemberType_Enumeration:
      +      case MemberType::Enumeration:
               type="enum"; break;
      -      case MemberType_EnumValue:
      +      case MemberType::EnumValue:
               type="econst"; break;
               //case MemberDef::Prototype:
               //  type="prototype"; break;
      -      case MemberType_Signal:
      +      case MemberType::Signal:
               type="signal"; break;
      -      case MemberType_Slot:
      +      case MemberType::Slot:
               type="slot"; break;
      -      case MemberType_Friend:
      +      case MemberType::Friend:
               type="ffunc"; break;
      -      case MemberType_DCOP:
      +      case MemberType::DCOP:
               type="dcop"; break;
      -      case MemberType_Property:
      +      case MemberType::Property:
               if (cd && cd->compoundType()==ClassDef::Protocol)
                 type="intfp";         // interface property
               else
                 type="instp";         // instance property
               break;
      -      case MemberType_Event:
      +      case MemberType::Event:
               type="event"; break;
      -      case MemberType_Interface:
      +      case MemberType::Interface:
               type="ifc"; break;
      -      case MemberType_Service:
      +      case MemberType::Service:
               type="svc"; break;
      -      case MemberType_Sequence:
      +      case MemberType::Sequence:
               type="sequence"; break;
      -      case MemberType_Dictionary:
      +      case MemberType::Dictionary:
               type="dictionary"; break;
           }
      -    cd = md->getClassDef();
      -    nd = md->getNamespaceDef();
      -    if (cd)
      -    {
      -      scope = cd->qualifiedName();
      -    }
      -    else if (nd)
      -    {
      -      scope = nd->name();
      -    }
      +    scope = md->getScopeString();
           fd = md->getFileDef();
           if (fd)
           {
      diff --git a/src/docsets.h b/src/docsets.h
      index a8c3d78b0a3..236761ae475 100644
      --- a/src/docsets.h
      +++ b/src/docsets.h
      @@ -50,7 +50,8 @@ class DocSets : public IndexIntf
                                const QCString &anchor,
                                bool separateIndex,
                                bool addToNavIndex,
      -                         const Definition *def
      +                         const Definition *def,
      +                         const QCString &nameAsHtml
                               );
           void addIndexItem(const Definition *context,const MemberDef *md,
                             const QCString §ionAnchor,const QCString &title);
      diff --git a/src/doctokenizer.h b/src/doctokenizer.h
      index d4097f62602..706c0a9984e 100644
      --- a/src/doctokenizer.h
      +++ b/src/doctokenizer.h
      @@ -26,50 +26,110 @@
       #include "qcstring.h"
       #include "construct.h"
       
      -enum Tokens
      +#define TOKEN_SPECIFICATIONS \
      +  TKSPEC(TK_EOF,          -1)                                   \
      +  TKSPEC(TK_NONE,          0)                                   \
      +  TKSPEC(TK_WORD,          1)                                   \
      +  TKSPEC(TK_LNKWORD,       2)                                   \
      +  TKSPEC(TK_WHITESPACE,    3)                                   \
      +  TKSPEC(TK_LISTITEM,      4)                                   \
      +  TKSPEC(TK_ENDLIST,       5)                                   \
      +  TKSPEC(TK_COMMAND_AT,    6) /*! Command starting with `@` */  \
      +  TKSPEC(TK_HTMLTAG,       7)                                   \
      +  TKSPEC(TK_SYMBOL,        8)                                   \
      +  TKSPEC(TK_NEWPARA,       9)                                   \
      +  TKSPEC(TK_RCSTAG,       10)                                   \
      +  TKSPEC(TK_URL,          11)                                   \
      +  TKSPEC(TK_COMMAND_BS,   12) /*! Command starting with `\` */
      +
      +#define RETVAL_SPECIFICATIONS \
      +  TKSPEC(RetVal_OK,              0x10000)  \
      +  TKSPEC(RetVal_SimpleSec,       0x10001)  \
      +  TKSPEC(RetVal_ListItem,        0x10002)  \
      +  TKSPEC(RetVal_Section,         0x10003)  \
      +  TKSPEC(RetVal_Subsection,      0x10004)  \
      +  TKSPEC(RetVal_Subsubsection,   0x10005)  \
      +  TKSPEC(RetVal_Paragraph,       0x10006)  \
      +  TKSPEC(RetVal_SubParagraph,    0x10007)  \
      +  TKSPEC(RetVal_EndList,         0x10008)  \
      +  TKSPEC(RetVal_EndPre,          0x10009)  \
      +  TKSPEC(RetVal_DescData,        0x1000A)  \
      +  TKSPEC(RetVal_DescTitle,       0x1000B)  \
      +  TKSPEC(RetVal_EndDesc,         0x1000C)  \
      +  TKSPEC(RetVal_TableRow,        0x1000D)  \
      +  TKSPEC(RetVal_TableCell,       0x1000E)  \
      +  TKSPEC(RetVal_TableHCell,      0x1000F)  \
      +  TKSPEC(RetVal_EndTable,        0x10010)  \
      +  TKSPEC(RetVal_EndTableCell,    0x10011)  \
      +  TKSPEC(RetVal_EndTableRow,     0x10012)  \
      +  TKSPEC(RetVal_Internal,        0x10013)  \
      +  TKSPEC(RetVal_SwitchLang,      0x10014)  \
      +  TKSPEC(RetVal_CloseXml,        0x10015)  \
      +  TKSPEC(RetVal_EndBlockQuote,   0x10016)  \
      +  TKSPEC(RetVal_CopyDoc,         0x10017)  \
      +  TKSPEC(RetVal_EndInternal,     0x10018)  \
      +  TKSPEC(RetVal_EndParBlock,     0x10019)  \
      +  TKSPEC(RetVal_EndHtmlDetails,  0x1001A)  \
      +  TKSPEC(RetVal_SubSubParagraph, 0x1001B)
      +
      +enum class TokenRetval
       {
      -  TK_WORD          = 1,
      -  TK_LNKWORD       = 2,
      -  TK_WHITESPACE    = 3,
      -  TK_LISTITEM      = 4,
      -  TK_ENDLIST       = 5,
      -  TK_COMMAND_AT    = 6, //! Command starting with `@`
      -  TK_HTMLTAG       = 7,
      -  TK_SYMBOL        = 8,
      -  TK_NEWPARA       = 9,
      -  TK_RCSTAG        = 10,
      -  TK_URL           = 11,
      -  TK_COMMAND_BS    = 12, //! Command starting with `\`
      -
      -  RetVal_OK             = 0x10000,
      -  RetVal_SimpleSec      = 0x10001,
      -  RetVal_ListItem       = 0x10002,
      -  RetVal_Section        = 0x10003,
      -  RetVal_Subsection     = 0x10004,
      -  RetVal_Subsubsection  = 0x10005,
      -  RetVal_Paragraph      = 0x10006,
      -  RetVal_SubParagraph   = 0x10007,
      -  RetVal_EndList        = 0x10008,
      -  RetVal_EndPre         = 0x10009,
      -  RetVal_DescData       = 0x1000A,
      -  RetVal_DescTitle      = 0x1000B,
      -  RetVal_EndDesc        = 0x1000C,
      -  RetVal_TableRow       = 0x1000D,
      -  RetVal_TableCell      = 0x1000E,
      -  RetVal_TableHCell     = 0x1000F,
      -  RetVal_EndTable       = 0x10010,
      -  RetVal_Internal       = 0x10011,
      -  RetVal_SwitchLang     = 0x10012,
      -  RetVal_CloseXml       = 0x10013,
      -  RetVal_EndBlockQuote  = 0x10014,
      -  RetVal_CopyDoc        = 0x10015,
      -  RetVal_EndInternal    = 0x10016,
      -  RetVal_EndParBlock    = 0x10017,
      -  RetVal_EndHtmlDetails = 0x10018,
      -  RetVal_SubSubParagraph= 0x10019,
      +#define TKSPEC(x,y) x = y,
      +      TOKEN_SPECIFICATIONS
      +      RETVAL_SPECIFICATIONS
      +#undef TKSPEC
       };
       
      -#define TK_COMMAND_CHAR(token) ((token)==TK_COMMAND_AT ? '@' : '\\')
      +class Token
      +{
      +  public:
      +    explicit Token(TokenRetval tv) : m_value(tv) {}
      +    TokenRetval value() const { return m_value; }
      +#define TKSPEC(x,y) static Token make_##x() { return Token(TokenRetval::x); }
      +    TOKEN_SPECIFICATIONS
      +    RETVAL_SPECIFICATIONS
      +#undef TKSPEC
      +
      +    const char *to_string() const
      +    {
      +      const char *result = "ERROR";
      +      switch (m_value)
      +      {
      +#define TKSPEC(x,y) case TokenRetval::x: result = #x; break;
      +        TOKEN_SPECIFICATIONS
      +        RETVAL_SPECIFICATIONS
      +#undef TKSPEC
      +      }
      +      return result;
      +    }
      +
      +    char command_to_char() const
      +    {
      +      return m_value==TokenRetval::TK_COMMAND_AT ? '@' : '\\';
      +    }
      +
      +    static Token char_to_command(char c)
      +    {
      +      return c=='@' ? make_TK_COMMAND_AT() : make_TK_COMMAND_BS();
      +    }
      +
      +    template
      +    bool is_any_of(ARGS... args) const
      +    {
      +      return ((m_value == args) || ...);
      +    }
      +
      +    bool is(TokenRetval rv) const
      +    {
      +      return m_value==rv;
      +    }
      +
      +    friend inline bool operator==(const Token &t1,const Token &t2) { return t1.m_value==t2.m_value; }
      +    friend inline bool operator!=(const Token &t1,const Token &t2) { return !(operator==(t1,t2)); }
      +
      +  private:
      +    TokenRetval m_value;
      +};
       
       /** @brief Data associated with a token used by the comment block parser. */
       struct TokenInfo
      @@ -129,12 +189,10 @@ class DocTokenizer
           TokenInfo *token();
           [[maybe_unused]] TokenInfo *resetToken();
       
      -    // helper functions
      -    static const char *tokToString(int token);
      -    static const char *retvalToString(int retval);
      -
           void setLineNr(int lineno);
           int getLineNr(void);
      +    void pushState();
      +    void popState();
       
           // operations on the scanner
           void findSections(const QCString &input,const Definition *d,
      @@ -144,7 +202,7 @@ class DocTokenizer
           void cleanup();
           void pushContext();
           bool popContext();
      -    int  lex();
      +    Token  lex();
           void unputString(const QCString &tag);
           void setStatePara();
           void setStateTitle();
      @@ -167,6 +225,7 @@ class DocTokenizer
           void setStateParam();
           void setStateXRefItem();
           void setStateFile();
      +    void setStateIFile();
           void setStatePattern();
           void setStateLink();
           void setStateCite();
      diff --git a/src/doctokenizer.l b/src/doctokenizer.l
      index 0d250e0d67a..4b27cc577af 100644
      --- a/src/doctokenizer.l
      +++ b/src/doctokenizer.l
      @@ -48,12 +48,11 @@ typedef yyguts_t *yyscan_t;
       #include "regex.h"
       #include "debug.h"
       #include "docnode.h"
      +#include "stringutil.h"
       
       #define YY_NO_INPUT 1
       #define YY_NO_UNISTD_H 1
       
      -#define TK_COMMAND_SEL() (yytext[0] == '@' ? TK_COMMAND_AT : TK_COMMAND_BS)
      -
       //--------------------------------------------------------------------------
       
       struct DocLexerContext
      @@ -81,6 +80,7 @@ struct doctokenizerYY_state
         int sharpCount=0;
         bool markdownSupport=true;
         bool insideHtmlLink=false;
      +  bool expectQuote=false;
       
         // context for section finding phase
         const Definition  *definition = nullptr;
      @@ -90,6 +90,7 @@ struct doctokenizerYY_state
         QCString     endMarker;
         int          autoListLevel;
         std::stack< std::unique_ptr > lexerStack;
      +  std::stack stateStack;
       
         int yyLineNr = 0;
       };
      @@ -123,54 +124,6 @@ QCString extractPartAfterNewLine(const QCString &text)
       
       //--------------------------------------------------------------------------
       
      -const char *DocTokenizer::tokToString(int token)
      -{
      -  switch (token)
      -  {
      -    case 0:              return "TK_EOF";
      -    case TK_WORD:        return "TK_WORD";
      -    case TK_LNKWORD:     return "TK_LNKWORD";
      -    case TK_WHITESPACE:  return "TK_WHITESPACE";
      -    case TK_LISTITEM:    return "TK_LISTITEM";
      -    case TK_ENDLIST:     return "TK_ENDLIST";
      -    case TK_COMMAND_AT:  return "TK_COMMAND_AT";
      -    case TK_HTMLTAG:     return "TK_HTMLTAG";
      -    case TK_SYMBOL:      return "TK_SYMBOL";
      -    case TK_NEWPARA:     return "TK_NEWPARA";
      -    case TK_RCSTAG:      return "TK_RCSTAG";
      -    case TK_URL:         return "TK_URL";
      -    case TK_COMMAND_BS:  return "TK_COMMAND_BS";
      -  }
      -  return "ERROR";
      -}
      -
      -const char *DocTokenizer::retvalToString(int retval)
      -{
      -  switch (retval)
      -  {
      -    case RetVal_OK:              return "RetVal_OK";
      -    case RetVal_SimpleSec:       return "RetVal_SimpleSec";
      -    case RetVal_ListItem:        return "RetVal_ListItem";
      -    case RetVal_Section:         return "RetVal_Section";
      -    case RetVal_Subsection:      return "RetVal_Subsection";
      -    case RetVal_Subsubsection:   return "RetVal_Subsubsection";
      -    case RetVal_Paragraph:       return "RetVal_Paragraph";
      -    case RetVal_SubParagraph:    return "RetVal_SubParagraph";
      -    case RetVal_SubSubParagraph: return "RetVal_SubSubParagraph";
      -    case RetVal_EndList:         return "RetVal_EndList";
      -    case RetVal_EndPre:          return "RetVal_EndPre";
      -    case RetVal_DescData:        return "RetVal_DescData";
      -    case RetVal_DescTitle:       return "RetVal_DescTitle";
      -    case RetVal_EndDesc:         return "RetVal_EndDesc";
      -    case RetVal_TableRow:        return "RetVal_TableRow";
      -    case RetVal_TableCell:       return "RetVal_TableCell";
      -    case RetVal_TableHCell:      return "RetVal_TableHCell";
      -    case RetVal_EndTable:        return "RetVal_EndTable";
      -    case RetVal_Internal:        return "RetVal_Internal";
      -  }
      -  return "ERROR";
      -}
      -
       static int computeIndent(const char *str,size_t length)
       {
         if (str==0 || length==std::string::npos) return 0;
      @@ -187,7 +140,7 @@ static int computeIndent(const char *str,size_t length)
           {
             indent=0;
           }
      -    else if (str[i]=='\\' && qstrncmp(str+i+1,"ilinebr",7)==0)
      +    else if (literal_at(&str[i],"\\ilinebr"))
           {
             indent=0;
             i+=7;
      @@ -204,46 +157,6 @@ static int computeIndent(const char *str,size_t length)
       
       //--------------------------------------------------------------------------
       
      -static QCString stripEmptyLines(const QCString &s)
      -{
      -  if (s.isEmpty()) return QCString();
      -  int end=static_cast(s.length());
      -  int start=0,p=0;
      -  // skip leading empty lines
      -  for (;;)
      -  {
      -    int c;
      -    while ((c=s[p]) && (c==' ' || c=='\t')) p++;
      -    if (s[p]=='\n')
      -    {
      -      start=++p;
      -    }
      -    else
      -    {
      -      break;
      -    }
      -  }
      -  // skip trailing empty lines
      -  p=end-1;
      -  if (p>=start && s.at(p)=='\n') p--;
      -  while (p>=start)
      -  {
      -    int c;
      -    while ((c=s[p]) && (c==' ' || c=='\t')) p--;
      -    if (s[p]=='\n')
      -    {
      -      end=p;
      -    }
      -    else
      -    {
      -      break;
      -    }
      -    p--;
      -  }
      -  //printf("stripEmptyLines(%d-%d)\n",start,end);
      -  return s.mid(start,end-start);
      -}
      -
       #define unput_string(yytext,yyleng) do { for (int i=(int)yyleng-1;i>=0;i--) unput(yytext[i]); } while(0)
       //--------------------------------------------------------------------------
       
      @@ -255,19 +168,21 @@ static inline const char *getLexerFILE() {return __FILE__;}
       #include "doxygen_lex.h"
       
       //--------------------------------------------------------------------------
      -//#define REAL_YY_DECL int doctokenizerYYlex (void)
      -//#define YY_DECL static int local_doctokenizer(void)
      -//#define LOCAL_YY_DECL local_doctokenizer()
      +#define YY_DECL static Token doctokenizerYYlex(yyscan_t yyscanner)
      +//#define LOCAL_YY_DECL local_doctokenizer(yyscan_t yyscanner)
      +#ifndef yyterminate
      +#define yyterminate() return Token::make_TK_EOF()
      +#endif
       
       %}
       
       CMD       ("\\"|"@")
       WS        [ \t\r\n]
      -NONWS     [^ \t\r\n]
       BLANK     [ \t\r]
       BLANKopt  {BLANK}*
       ID        [$a-z_A-Z\x80-\xFF][$a-z_A-Z0-9\x80-\xFF]*
       LABELID   [a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF\-]*
      +CODEID    [a-zA-Z][a-zA-Z0-9+]*
       PHPTYPE   [?]?[\\:a-z_A-Z0-9\x80-\xFF\-]+
       CITESCHAR [a-z_A-Z0-9\x80-\xFF\-\?]
       CITEECHAR [a-z_A-Z0-9\x80-\xFF\-\+:\/\?]
      @@ -276,7 +191,6 @@ DOXYCFG   [A-Z][A-Z_0-9]*
       MAILADDR  ("mailto:")?[a-z_A-Z0-9\x80-\xFF.+-]+"@"[a-z_A-Z0-9\x80-\xFf-]+("."[a-z_A-Z0-9\x80-\xFf\-]+)+[a-z_A-Z0-9\x80-\xFf\-]+
       MAILWS    [\t a-z_A-Z0-9\x80-\xFF+-]
       MAILADDR2 {MAILWS}+{BLANK}+("at"|"AT"|"_at_"|"_AT_"){BLANK}+{MAILWS}+("dot"|"DOT"|"_dot_"|"_DOT_"|"point"|"POINT"|"_point_"|"_POINT_"){BLANK}+{MAILWS}+
      -OPTSTARS  ("/""/"{BLANK}*)?"*"*{BLANK}*
       LISTITEM  {BLANK}*[-]("#")?{WS}
       MLISTITEM {BLANK}*[+*]{WS}
       OLISTITEM {BLANK}*("0"|[1-9][0-9]*)"."{BLANK}
      @@ -287,8 +201,8 @@ ATTRIB    {ATTRNAME}{WS}*("="{WS}*(("\""[^\"]*"\"")|("'"[^\']*"'")|[^ \t\r\n'"><
       URLCHAR   [a-z_A-Z0-9\!\~\,\:\;\'\$\?\@\&\%\#\.\-\+\/\=\x80-\xFF]
       URLMASK   ({URLCHAR}+([({]{URLCHAR}*[)}])?)+
       URLPROTOCOL ("http:"|"https:"|"ftp:"|"ftps:"|"sftp:"|"file:"|"news:"|"irc:"|"ircs:")
      -FILEICHAR [a-z_A-Z0-9\x80-\xFF\\:\\\/\-\+=&#@]
      -FILEECHAR [a-z_A-Z0-9\x80-\xFF\-\+=&#@]
      +FILEICHAR [a-z_A-Z0-9\x80-\xFF\\:\\\/\-\+=&#@~]
      +FILEECHAR [a-z_A-Z0-9\x80-\xFF\-\+=&#@~]
       FILECHARS {FILEICHAR}*{FILEECHAR}+
       HFILEMASK {FILEICHAR}*("."{FILEICHAR}+)+{FILECHARS}*
       VFILEMASK {FILECHARS}("."{FILECHARS})*
      @@ -296,7 +210,7 @@ FILEMASK  {VFILEMASK}|{HFILEMASK}
       LINKMASK  [^ \t\n\r\\@<&${}]+("("[^\n)]*")")?({BLANK}*("const"|"volatile"){BLANK}+)?
       VERBATIM  "verbatim"{BLANK}*
       SPCMD1    {CMD}([a-z_A-Z][a-z_A-Z0-9]*|{VERBATIM}|"--"|"---")
      -SPCMD2    {CMD}[\\@<>&$#%~".+=|-]
      +SPCMD2    {CMD}[\\@<>&$#%~".+=|!?-]
       SPCMD3    {CMD}_form#[0-9]+
       SPCMD4    {CMD}"::"
       SPCMD5    {CMD}":"
      @@ -312,8 +226,10 @@ ANONNS    "anonymous_namespace{"[^}]*"}"
       SCOPEPRE  (({ID}{TEMPLPART}?)|{ANONNS}){SCOPESEP}
       SCOPEKEYS ":"({ID}":")*
       SCOPECPP  {SCOPEPRE}*(~)?{ID}{TEMPLPART}?
      +SCOPECPPN {SCOPEPRE}*(~)?{ID}
       SCOPEOBJC {SCOPEPRE}?{ID}{SCOPEKEYS}?
       SCOPEMASK {SCOPECPP}|{SCOPEOBJC}
      +SCOPEMSKN {SCOPECPPN}|{SCOPEOBJC}
       FUNCARG   "("{FUNCPART}")"({BLANK}*("volatile"|"const"){BLANK})?
       FUNCARG2  "("{FUNCPART}")"({BLANK}*("volatile"|"const"))?
       OPNEW     {BLANK}+"new"({BLANK}*"[]")?
      @@ -324,6 +240,7 @@ OPMASK    ({BLANK}*{OPNORM}{FUNCARG})
       OPMASKOPT ({BLANK}*{OPNORM}{FUNCARG}?)|({OPCAST}{FUNCARG})
       OPMASKOP2 ({BLANK}*{OPNORM}{FUNCARG2}?)|({OPCAST}{FUNCARG2})
       LNKWORD1  ("::"|"#")?{SCOPEMASK}
      +LNKWORDN  ("::"|"#")?{SCOPEMSKN}
       CVSPEC    {BLANK}*("const"|"volatile")
       LNKWORD2  (({SCOPEPRE}*"operator"{OPMASK})|({SCOPEPRE}"operator"{OPMASKOPT})|(("::"|"#"){SCOPEPRE}*"operator"{OPMASKOPT})){CVSPEC}?
       LNKWORD3  ([0-9a-z_A-Z\-]+("/"|"\\"))*[0-9a-z_A-Z\-]+("."[0-9a-z_A-Z]+)+
      @@ -386,6 +303,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
       %x St_XRefItem
       %x St_XRefItem2
       %x St_File
      +%x St_IFile
       %x St_Pattern
       %x St_Link
       %x St_Cite
      @@ -427,7 +345,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                yyextra->token.isCheckedList = false;
                                yyextra->token.id         = -1;
                                yyextra->token.indent     = computeIndent(yytext,dashPos);
      -                         return TK_LISTITEM;
      +                         return Token::make_TK_LISTITEM();
                              }
       ^{CLISTITEM}  { /* checkbox item */
                                QCString text=yytext;
      @@ -438,7 +356,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                else if (text.find('X') != -1) yyextra->token.id = DocAutoList::Checked_X;
                                else yyextra->token.id = DocAutoList::Unchecked;
                                yyextra->token.indent     = computeIndent(yytext,dashPos);
      -                         return TK_LISTITEM;
      +                         return Token::make_TK_LISTITEM();
                              }
       ^{MLISTITEM}  { /* list item */
                                if (yyextra->insideHtmlLink || !yyextra->markdownSupport || yyextra->insidePre)
      @@ -458,7 +376,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                  yyextra->token.isCheckedList = false;
                                  yyextra->token.id         = -1;
                                  yyextra->token.indent     = computeIndent(yytext,listPos);
      -                           return TK_LISTITEM;
      +                           return Token::make_TK_LISTITEM();
                                }
                              }
       ^{OLISTITEM}  { /* numbered list item */
      @@ -481,23 +399,23 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                  yyextra->token.id = ok ? id : -1;
                                  if (!ok)
                                  {
      -                             warn(yyextra->fileName,yyextra->yyLineNr,"Invalid number for list item '%s' ",match.str().c_str());
      +                             warn(yyextra->fileName,yyextra->yyLineNr,"Invalid number for list item '{}' ",match.str());
                                  }
                                  yyextra->token.indent     = computeIndent(yytext,markPos);
      -                           return TK_LISTITEM;
      +                           return Token::make_TK_LISTITEM();
                                }
                              }
       {BLANK}*(\n|"\\ilinebr"){LISTITEM}     { /* list item on next line */
                                if (yyextra->insideHtmlLink || yyextra->insidePre) REJECT;
                                lineCount(yytext,yyleng);
      -                         QCString text=extractPartAfterNewLine(QCString(yytext));
      +                         QCString text=extractPartAfterNewLine(yytext);
                                uint32_t dashPos = static_cast(text.findRev('-'));
                                assert(dashPos!=static_cast(-1));
                                yyextra->token.isEnumList = text.at(dashPos+1)=='#';
                                yyextra->token.isCheckedList = false;
                                yyextra->token.id         = -1;
                                yyextra->token.indent     = computeIndent(text.data(),dashPos);
      -                         return TK_LISTITEM;
      +                         return Token::make_TK_LISTITEM();
                              }
       {BLANK}*\n{CLISTITEM}   { /* checkbox item on next line */
                                QCString text=yytext;
      @@ -509,7 +427,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                else if (text.find('X') != -1) yyextra->token.id = DocAutoList::Checked_X;
                                else yyextra->token.id = DocAutoList::Unchecked;
                                yyextra->token.indent     = computeIndent(text.data(),dashPos);
      -                         return TK_LISTITEM;
      +                         return Token::make_TK_LISTITEM();
                              }
       {BLANK}*(\n|"\\ilinebr"){MLISTITEM}     { /* list item on next line */
                                if (yyextra->insideHtmlLink || !yyextra->markdownSupport || yyextra->insidePre)
      @@ -519,7 +437,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                else
                                {
                                  lineCount(yytext,yyleng);
      -                           std::string text=extractPartAfterNewLine(QCString(yytext)).str();
      +                           std::string text=extractPartAfterNewLine(yytext).str();
                                  static const reg::Ex re(R"([*+][^*+]*$)"); // find last + or *
                                  reg::Match match;
                                  reg::search(text,match,re);
      @@ -529,7 +447,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                  yyextra->token.isCheckedList = false;
                                  yyextra->token.id         = -1;
                                  yyextra->token.indent     = computeIndent(text.c_str(),markPos);
      -                           return TK_LISTITEM;
      +                           return Token::make_TK_LISTITEM();
                                }
                              }
       {BLANK}*(\n|"\\ilinebr"){OLISTITEM}     { /* list item on next line */
      @@ -540,7 +458,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                else
                                {
                                  lineCount(yytext,yyleng);
      -                           std::string text=extractPartAfterNewLine(QCString(yytext)).str();
      +                           std::string text=extractPartAfterNewLine(yytext).str();
                                  static const reg::Ex re(R"(\d+)");
                                  reg::Match match;
                                  reg::search(text,match,re);
      @@ -553,10 +471,10 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                  yyextra->token.id = ok ? id : -1;
                                  if (!ok)
                                  {
      -                             warn(yyextra->fileName,yyextra->yyLineNr,"Invalid number for list item '%s' ",match.str().c_str());
      +                             warn(yyextra->fileName,yyextra->yyLineNr,"Invalid number for list item '{}' ",match.str());
                                  }
                                  yyextra->token.indent     = computeIndent(text.c_str(),markPos);
      -                           return TK_LISTITEM;
      +                           return Token::make_TK_LISTITEM();
                                }
                              }
       ^{ENDLIST}       { /* end list */
      @@ -564,27 +482,27 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                lineCount(yytext,yyleng);
                                size_t dotPos = static_cast(QCString(yytext).findRev('.'));
                                yyextra->token.indent     = computeIndent(yytext,dotPos);
      -                         return TK_ENDLIST;
      +                         return Token::make_TK_ENDLIST();
                              }
       {BLANK}*(\n|"\\ilinebr"){ENDLIST}      { /* end list on next line */
                                if (yyextra->insideHtmlLink || yyextra->insidePre) REJECT;
                                lineCount(yytext,yyleng);
      -                         QCString text=extractPartAfterNewLine(QCString(yytext));
      +                         QCString text=extractPartAfterNewLine(yytext);
                                size_t dotPos = static_cast(text.findRev('.'));
                                yyextra->token.indent = computeIndent(text.data(),dotPos);
      -                         return TK_ENDLIST;
      +                         return Token::make_TK_ENDLIST();
                              }
       "{"{BLANK}*"@linkplain"/{WS}+ {
                                yyextra->token.name = "javalinkplain";
      -                         return TK_COMMAND_AT;
      +                         return Token::make_TK_COMMAND_AT();
                              }
       "{"{BLANK}*"@link"/{WS}+ {
                                yyextra->token.name = "javalink";
      -                         return TK_COMMAND_AT;
      +                         return Token::make_TK_COMMAND_AT();
                              }
       "{"{BLANK}*"@inheritDoc"{BLANK}*"}" {
                                yyextra->token.name = "inheritdoc";
      -                         return TK_COMMAND_AT;
      +                         return Token::make_TK_COMMAND_AT();
                              }
       "@_fakenl"    { // artificial new line
                                //yyextra->yyLineNr++;
      @@ -594,7 +512,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                bool ok;
                                yyextra->token.id = QCString(yytext).right((int)yyleng-7).toInt(&ok);
                                ASSERT(ok);
      -                         return TK_COMMAND_SEL();
      +                         return Token::char_to_command(yytext[0]);
                              }
       {CMD}"n"\n    { /* \n followed by real newline */
                                lineCount(yytext,yyleng);
      @@ -602,7 +520,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                yyextra->token.name = yytext+1;
                                yyextra->token.name = yyextra->token.name.stripWhiteSpace();
                                yyextra->token.paramDir=TokenInfo::Unspecified;
      -                         return TK_COMMAND_SEL();
      +                         return Token::char_to_command(yytext[0]);
                              }
       "\\ilinebr"   {
                              }
      @@ -613,7 +531,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                yyextra->token.name = yytext+1;
                                yyextra->token.name = yyextra->token.name.stripWhiteSpace();
                                yyextra->token.paramDir=TokenInfo::Unspecified;
      -                         return TK_COMMAND_SEL();
      +                         return Token::char_to_command(yytext[0]);
                              }
       {PARAMIO}     { /* param [in,out] command */
                                yyextra->token.name = "param";
      @@ -639,40 +557,40 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                {
                                  yyextra->token.paramDir=TokenInfo::Unspecified;
                                }
      -                         return TK_COMMAND_SEL();
      +                         return Token::char_to_command(yytext[0]);
                              }
       {URLPROTOCOL}{URLMASK}/[,\.] { // URL, or URL.
                                yyextra->token.name=yytext;
                                yyextra->token.isEMailAddr=FALSE;
      -                         return TK_URL;
      +                         return Token::make_TK_URL();
                              }
       {URLPROTOCOL}{URLMASK} { // URL
                                yyextra->token.name=yytext;
                                yyextra->token.isEMailAddr=FALSE;
      -                         return TK_URL;
      +                         return Token::make_TK_URL();
                              }
       "<"{URLPROTOCOL}{URLMASK}">" { // URL
                                yyextra->token.name=yytext;
                                yyextra->token.name = yyextra->token.name.mid(1,yyextra->token.name.length()-2);
                                yyextra->token.isEMailAddr=FALSE;
      -                         return TK_URL;
      +                         return Token::make_TK_URL();
                              }
       {MAILADDR}    { // Mail address
                                yyextra->token.name=yytext;
                                yyextra->token.name.stripPrefix("mailto:");
                                yyextra->token.isEMailAddr=TRUE;
      -                         return TK_URL;
      +                         return Token::make_TK_URL();
                              }
       "<"{MAILADDR}">" { // Mail address
                                yyextra->token.name=yytext;
                                yyextra->token.name = yyextra->token.name.mid(1,yyextra->token.name.length()-2);
                                yyextra->token.name.stripPrefix("mailto:");
                                yyextra->token.isEMailAddr=TRUE;
      -                         return TK_URL;
      +                         return Token::make_TK_URL();
                              }
       "<"{MAILADDR2}">" { // anti spam mail address
                                yyextra->token.name=yytext;
      -                         return TK_WORD;
      +                         return Token::make_TK_WORD();
                              }
       {RCSID} { /* RCS tag */
                                QCString tagName(yytext+1);
      @@ -690,7 +608,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                  }
                                }
                                yyextra->token.text = tagName.mid(text_begin,text_end-text_begin);
      -                         return TK_RCSTAG;
      +                         return Token::make_TK_RCSTAG();
                              }
       "$("{ID}")"   | /* environment variable */
       "$("{ID}"("{ID}"))"   { /* environment variable */
      @@ -699,41 +617,49 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[
                                QCString value = Portable::getenv(name);
                                for (int i=static_cast(value.length())-1;i>=0;i--) unput(value.at(i));
                              }
      +"
      ‍" { + // for a markdown inserted block quote, + // tell flex that after putting the last indent + // back we are at the beginning of the line, see issue #11309 + YY_CURRENT_BUFFER->yy_at_bol=1; + lineCount(yytext,yyleng); + handleHtmlTag(yyscanner,yytext); + return Token::make_TK_HTMLTAG(); + } {HTMLTAG} { /* html tag */ lineCount(yytext,yyleng); handleHtmlTag(yyscanner,yytext); - return TK_HTMLTAG; + return Token::make_TK_HTMLTAG(); } "&"{ID}";" { /* special symbol */ yyextra->token.name = yytext; - return TK_SYMBOL; + return Token::make_TK_SYMBOL(); } /********* patterns for linkable words ******************/ -{ID}/"<"{HTMLKEYW}">" { /* this rule is to prevent opening html +{ID}/"<"{HTMLKEYW}">"+ { /* this rule is to prevent opening html * tag to be recognized as a templated classes */ yyextra->token.name = yytext; - return TK_LNKWORD; - } -{LNKWORD1}/"" { // prevent html tag to be parsed as template arguments + return Token::make_TK_LNKWORD(); + } +{LNKWORDN}/("<"{HTMLKEYW}">")+ { // prevent
      html tag to be parsed as template arguments yyextra->token.name = yytext; - return TK_LNKWORD; + return Token::make_TK_LNKWORD(); } -{LNKWORD1}/"
      " | // prevent
      html tag to be parsed as template arguments -{LNKWORD1} | -{LNKWORD1}{FUNCARG} | -{LNKWORD2} | +{LNKWORD1} | +{LNKWORD1}{FUNCARG} | +{LNKWORD2} | {LNKWORD3} { yyextra->token.name = yytext; - return TK_LNKWORD; + return Token::make_TK_LNKWORD(); } {LNKWORD1}{FUNCARG}{CVSPEC}[^a-z_A-Z0-9] { yyextra->token.name = yytext; yyextra->token.name = yyextra->token.name.left(yyextra->token.name.length()-1); unput(yytext[(int)yyleng-1]); - return TK_LNKWORD; + return Token::make_TK_LNKWORD(); } /********* patterns for normal words ******************/ @@ -746,16 +672,16 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.name = &yytext[1]; else yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } ({ID}".")+{ID} { yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } "operator"/{BLANK}*"<"[a-zA-Z_0-9]+">" { // Special case: word "operator" followed by a HTML command // avoid interpretation as "operator <" yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } /*******************************************************/ @@ -764,11 +690,11 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ {BLANK}*\n{BLANK}* { /* white space */ lineCount(yytext,yyleng); yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } [\\@<>&$#%~] { yyextra->token.name = yytext; - return TK_COMMAND_SEL(); + return Token::char_to_command(yytext[0]); } ({BLANK}*\n)+{BLANK}*\n/{LISTITEM} { /* skip trailing paragraph followed by new list item */ if (yyextra->insidePre || yyextra->autoListLevel==0) @@ -803,7 +729,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ if (yyextra->insidePre) { yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } else { @@ -819,16 +745,16 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ // back we are at the beginning of the line YY_CURRENT_BUFFER->yy_at_bol=1; // start of a new paragraph - return TK_NEWPARA; + return Token::make_TK_NEWPARA(); } } -{BLANK}*"{"(".")?{LABELID}"}" { +{BLANK}*"{"(".")?{CODEID}"}" { yyextra->token.name = yytext; int i=yyextra->token.name.find('{'); /* } to keep vi happy */ yyextra->token.name = yyextra->token.name.mid(i+1,yyextra->token.name.length()-i-2); BEGIN(St_Code); } -{BLANK}*"{"(".")?{LABELID}"}" { +{BLANK}*"{"(".")?{CODEID}"}" { yyextra->token.name = yytext; int i=yyextra->token.name.find('{'); /* } to keep vi happy */ yyextra->token.name = yyextra->token.name.mid(i+1,yyextra->token.name.length()-i-2); @@ -848,15 +774,15 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } {WS}*{CMD}"endcode" { lineCount(yytext,yyleng); - return RetVal_OK; + return Token::make_RetVal_OK(); } {WS}*{CMD}"endicode" { lineCount(yytext,yyleng); - return RetVal_OK; + return Token::make_RetVal_OK(); } {WS}*"" { lineCount(yytext,yyleng); - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n<]+ | \n | @@ -877,7 +803,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ BEGIN(St_HtmlOnly); } {CMD}"endhtmlonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n$]+ | \n | @@ -886,7 +812,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"endmanonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n$]+ | \n | @@ -895,7 +821,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"endrtfonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n$]+ | \n | @@ -904,7 +830,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"endlatexonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -913,7 +839,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"endxmlonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -922,7 +848,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"enddocbookonly" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -931,17 +857,17 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}"endverbatim" { - yyextra->token.verb=stripEmptyLines(yyextra->token.verb); - return RetVal_OK; + yyextra->token.verb = yyextra->token.verb.stripLeadingAndTrailingEmptyLines(); + return Token::make_RetVal_OK(); } {CMD}"endiliteral " { // note extra space as this is artificially added // remove spaces that have been added - yyextra->token.verb=yyextra->token.verb.mid(1,yyextra->token.verb.length()-2); - return RetVal_OK; + yyextra->token.verb = yyextra->token.verb.mid(1,yyextra->token.verb.length()-2); + return Token::make_RetVal_OK(); } {CMD}"endiverbatim" { - yyextra->token.verb=stripEmptyLines(yyextra->token.verb); - return RetVal_OK; + yyextra->token.verb = yyextra->token.verb.stripLeadingAndTrailingEmptyLines(); + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -951,17 +877,17 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } {BLANK}*"{"[a-zA-Z_,:0-9\. ]*"}" { // option(s) present yyextra->token.verb = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } "\\ilinebr" | "\n" | . { yyextra->token.sectionId = ""; unput_string(yytext,yyleng); - return RetVal_OK; + return Token::make_RetVal_OK(); } {CMD}"enddot" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -970,7 +896,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.verb+=yytext; } {CMD}("endmsc") { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -980,33 +906,33 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } {BLANK}*"{"[a-zA-Z_,:0-9\. ]*"}" { // case 1: options present yyextra->token.sectionId = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } {BLANK}*{FILEMASK}{BLANK}+/{ID}"=" { // case 2: plain file name specified followed by an attribute yyextra->token.sectionId = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } {BLANK}*{FILEMASK}{BLANK}+/"\"" { // case 3: plain file name specified followed by a quoted title yyextra->token.sectionId = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } {BLANK}*{FILEMASK}{BLANKopt}/\n { // case 4: plain file name specified without title or attributes yyextra->token.sectionId = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } {BLANK}*{FILEMASK}{BLANKopt}/"\\ilinebr" { // case 5: plain file name specified without title or attributes yyextra->token.sectionId = QCString(yytext).stripWhiteSpace(); - return RetVal_OK; + return Token::make_RetVal_OK(); } "\\ilinebr" | "\n" | . { yyextra->token.sectionId = ""; unput_string(yytext,yyleng); - return RetVal_OK; + return Token::make_RetVal_OK(); } {CMD}"enduml" { - return RetVal_OK; + return Token::make_RetVal_OK(); } [^\\@\n]+ | \n | @@ -1019,7 +945,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } [ \t]+ { yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } . { // non-quoted title unput(*yytext); @@ -1027,41 +953,41 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } \n { unput(*yytext); - return 0; + return Token::make_TK_NONE(); } "\\ilinebr" { unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } "&"{ID}";" { /* symbol */ yyextra->token.name = yytext; - return TK_SYMBOL; + return Token::make_TK_SYMBOL(); } {HTMLTAG} { yyextra->token.name = yytext; handleHtmlTag(yyscanner,yytext); - return TK_HTMLTAG; + return Token::make_TK_HTMLTAG(); } \n { /* new line => end of title */ unput(*yytext); - return 0; + return Token::make_TK_NONE(); } "\\ilinebr" { /* new line => end of title */ unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } {SPCMD1} | {SPCMD2} { /* special command */ yyextra->token.name = yytext+1; yyextra->token.paramDir=TokenInfo::Unspecified; - return TK_COMMAND_SEL(); + return Token::char_to_command(yytext[0]); } {ID}"=" { /* attribute */ if (yytext[0]=='%') // strip % if present yyextra->token.name = &yytext[1]; else yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } [\-+0-9] | {WORD1} | @@ -1072,38 +998,38 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->token.name = &yytext[1]; else yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } [ \t]+ { yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } "&"{ID}";" { /* symbol */ yyextra->token.name = yytext; - return TK_SYMBOL; + return Token::make_TK_SYMBOL(); } (\n|"\\ilinebr") { /* new line => end of title */ unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } {SPCMD1} | {SPCMD2} { /* special command */ yyextra->token.name = yytext+1; yyextra->token.paramDir=TokenInfo::Unspecified; - return TK_COMMAND_SEL(); + return Token::char_to_command(yytext[0]); } {WORD1NQ} | {WORD2NQ} { /* word */ yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } [ \t]+ { yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } "\"" { /* closing quote => end of title */ BEGIN(St_TitleA); - return 0; + return Token::make_TK_NONE(); } {BLANK}*{ID}{BLANK}*"="{BLANK}* { // title attribute yyextra->token.name = yytext; @@ -1116,25 +1042,25 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ lineCount(yytext,yyleng); yyextra->token.chars = yytext; BEGIN(St_TitleN); - return TK_WORD; + return Token::make_TK_WORD(); } . { unput(*yytext); - return 0; + return Token::make_TK_NONE(); } (\n|"\\ilinebr") { unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } {LABELID}{WS}? { // anchor lineCount(yytext,yyleng); yyextra->token.name = QCString(yytext).stripWhiteSpace(); - return TK_WORD; + return Token::make_TK_WORD(); } . { unput(*yytext); - return 0; + return Token::make_TK_NONE(); } {CITEID} { // label to cite if (yytext[0] =='"') @@ -1146,71 +1072,72 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ { yyextra->token.name=yytext; } - return TK_WORD; + return Token::make_TK_WORD(); } {BLANK} { // white space unput(' '); - return 0; + return Token::make_TK_NONE(); } (\n|"\\ilinebr") { // new line unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } . { // any other character unput(*yytext); - return 0; + return Token::make_TK_NONE(); } {DOXYCFG} { // config option yyextra->token.name=yytext; - return TK_WORD; + return Token::make_TK_WORD(); } {BLANK} { // white space unput(' '); - return 0; + return Token::make_TK_NONE(); } (\n|"\\ilinebr") { // new line unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } . { // any other character unput(*yytext); - return 0; + return Token::make_TK_NONE(); } {REFWORD_NOCV}/{BLANK}("const")[a-z_A-Z0-9] { // see bug776988 yyextra->token.name=yytext; - return TK_WORD; + return Token::make_TK_WORD(); } {REFWORD_NOCV}/{BLANK}("volatile")[a-z_A-Z0-9] { // see bug776988 yyextra->token.name=yytext; - return TK_WORD; + return Token::make_TK_WORD(); } {REFWORD} { // label to refer to yyextra->token.name=yytext; - return TK_WORD; + return Token::make_TK_WORD(); } {BLANK} { // white space unput(' '); - return 0; + return Token::make_TK_NONE(); } {WS}+"\""{WS}* { // white space following by quoted string + yyextra->expectQuote=true; lineCount(yytext,yyleng); BEGIN(St_Ref2); } (\n|"\\ilinebr") { // new line unput_string(yytext,yyleng); - return 0; + return Token::make_TK_NONE(); } "\""[^"\n]+"\"" { // quoted first argument -> return without quotes yyextra->token.name=QCString(yytext).mid(1,yyleng-2); - return TK_WORD; + return Token::make_TK_WORD(); } . { // any other character unput(*yytext); - return 0; + return Token::make_TK_NONE(); } [A-Z_a-z0-9.:/#\-\+\(\)]+ { yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } {BLANK}+"\"" { BEGIN(St_Ref2); @@ -1218,7 +1145,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ ({SCOPEMASK}|{ANONNS}){BLANK}|{FILEMASK} { yyextra->token.name = yytext; yyextra->token.name = yyextra->token.name.stripWhiteSpace(); - return TK_WORD; + return Token::make_TK_WORD(); } {SCOPEMASK}"<" { yyextra->token.name = yytext; @@ -1237,7 +1164,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ yyextra->sharpCount--; if (yyextra->sharpCount<=0) { - return TK_WORD; + return Token::make_TK_WORD(); } } . { @@ -1245,32 +1172,39 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ } "&"{ID}";" { /* symbol */ yyextra->token.name = yytext; - return TK_SYMBOL; + return Token::make_TK_SYMBOL(); } -"\""|\n|"\\ilinebr" { /* " or \n => end of title */ +"\""|\n|"\\ilinebr" { /* " or \n => end of title? */ lineCount(yytext,yyleng); - return 0; + if (!yyextra->expectQuote || yytext[0]=='"') + { + return Token::make_TK_NONE(); + } + else + { + yyextra->token.name += yytext; + } } {HTMLTAG_STRICT} { /* html tag */ lineCount(yytext,yyleng); handleHtmlTag(yyscanner,yytext); - return TK_HTMLTAG; + return Token::make_TK_HTMLTAG(); } {SPCMD1} | {SPCMD2} { /* special command */ yyextra->token.name = yytext+1; yyextra->token.paramDir=TokenInfo::Unspecified; - return TK_COMMAND_SEL(); + return Token::char_to_command(yytext[0]); } {WORD1NQ} | {WORD2NQ} { /* word */ yyextra->token.name = yytext; - return TK_WORD; + return Token::make_TK_WORD(); } [ \t]+ { yyextra->token.chars=yytext; - return TK_WHITESPACE; + return Token::make_TK_WHITESPACE(); } {LABELID} { yyextra->token.name=yytext; @@ -1282,7 +1216,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ QCString numStr(yytext); numStr=numStr.left((int)yyleng-1); yyextra->token.id=numStr.toInt(); - return RetVal_OK; + return Token::make_RetVal_OK(); } "" { /* end of html comment */ BEGIN(yyextra->commentState); @@ -1443,7 +1392,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ . (\n|"\\ilinebr") { if (*yytext == '\n') unput('\n'); - return 0; + return Token::make_TK_NONE(); } /* State for the pass used to find the anchors and sections */ @@ -1603,7 +1552,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ BEGIN(St_Sections); } . { - warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '%s' while looking for section label or title",yytext); + warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '{}' while looking for section label or title",yytext); } [^\\\n]+ { @@ -1615,7 +1564,7 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ (\n|"\\ilinebr") { unput_string(yytext,yyleng); yyextra->token.name = yyextra->token.name.stripWhiteSpace(); - return TK_WORD; + return Token::make_TK_WORD(); } /* Generic rules that work for all states */ @@ -1626,12 +1575,12 @@ SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[ <*>"\\ilinebr" { } <*>[\\@<>&$#%~"=] { /* unescaped special character */ - //warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '%s', assuming command \\%s was meant.",yytext,yytext); + //warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '{}', assuming command \\{} was meant.",yytext,yytext); yyextra->token.name = yytext; - return TK_COMMAND_SEL(); + return Token::char_to_command(yytext[0]); } <*>. { - warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '%s'",yytext); + warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected character '{}'",yytext); } %% @@ -1650,7 +1599,7 @@ static int yyread(yyscan_t yyscanner,char *buf,int max_size) static void processSection(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; - //printf("%s: found section/anchor with name '%s'\n",qPrint(g_fileName),qPrint(g_secLabel)); + //printf("%s: found section/anchor with name '%s'\n",qPrint(yyextra->fileName),qPrint(yyextra->secLabel)); QCString file; if (yyextra->definition) { @@ -1658,7 +1607,7 @@ static void processSection(yyscan_t yyscanner) } else { - warn(yyextra->fileName,yyextra->yyLineNr,"Found section/anchor %s without context",qPrint(yyextra->secLabel)); + warn(yyextra->fileName,yyextra->yyLineNr,"Found section/anchor {} without context",yyextra->secLabel); } SectionInfo *si = SectionManager::instance().find(yyextra->secLabel); if (si) @@ -1716,8 +1665,8 @@ static void handleHtmlTag(yyscan_t yyscanner,const char *text) // search for end of name while (i<(int)yyleng && !isspace((uint8_t)c) && c!='=' && c!= '>') { c=tagText.at(++i); } int endName=i; - HtmlAttrib opt; - opt.name = tagText.mid(startName,endName-startName).lower(); + QCString optName,optValue; + optName = tagText.mid(startName,endName-startName).lower(); // skip spaces while (i<(int)yyleng && isspace((uint8_t)c)) { c=tagText.at(++i); } if (tagText.at(i)=='=') // option has value @@ -1753,20 +1702,20 @@ static void handleHtmlTag(yyscan_t yyscanner,const char *text) endAttrib=i; if (i<(int)yyleng) { c=tagText.at(++i);} } - opt.value = tagText.mid(startAttrib,endAttrib-startAttrib); - if (opt.name == "align") opt.value = opt.value.lower(); - else if (opt.name == "valign") + optValue = tagText.mid(startAttrib,endAttrib-startAttrib); + if (optName == "align") optValue = optValue.lower(); + else if (optName == "valign") { - opt.value = opt.value.lower(); - if (opt.value == "center") opt.value="middle"; + optValue = optValue.lower(); + if (optValue == "center") optValue="middle"; } } else // start next option { } //printf("=====> Adding option name=<%s> value=<%s>\n", - // qPrint(opt.name),qPrint(opt.value)); - yyextra->token.attribs.push_back(opt); + // qPrint(optName),qPrint(optValue)); + yyextra->token.attribs.emplace_back(optName,optValue); } yyextra->token.attribsStr = tagText.mid(startAttribList,i-startAttribList); } @@ -1829,7 +1778,7 @@ DocTokenizer::~DocTokenizer() doctokenizerYYlex_destroy(p->yyscanner); } -int DocTokenizer::lex() +Token DocTokenizer::lex() { return doctokenizerYYlex(p->yyscanner); } @@ -2072,6 +2021,13 @@ void DocTokenizer::setStateFile() BEGIN(St_File); } +void DocTokenizer::setStateIFile() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + BEGIN(St_IFile); +} + void DocTokenizer::setStatePattern() { yyscan_t yyscanner = p->yyscanner; @@ -2105,6 +2061,7 @@ void DocTokenizer::setStateRef() { yyscan_t yyscanner = p->yyscanner; struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->expectQuote=false; BEGIN(St_Ref); } @@ -2259,4 +2216,20 @@ int DocTokenizer::getLineNr(void) return yyextra->yyLineNr; } +void DocTokenizer::pushState() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->stateStack.push(YYSTATE); +} + +void DocTokenizer::popState() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + assert(!yyextra->stateStack.empty()); + BEGIN(yyextra->stateStack.top()); + yyextra->stateStack.pop(); +} + #include "doctokenizer.l.h" diff --git a/src/docvisitor.cpp b/src/docvisitor.cpp index 58c93b2837b..5df763d2b54 100644 --- a/src/docvisitor.cpp +++ b/src/docvisitor.cpp @@ -47,7 +47,7 @@ CodeParserInterface &DocVisitor::getCodeParser(const QCString &extension) if (it==m_p->parserFactoryMap.end()) { auto factory = Doxygen::parserManager->getCodeParserFactory(extension); - auto result = m_p->parserFactoryMap.insert(std::make_pair(ext,factory())); + auto result = m_p->parserFactoryMap.emplace(ext,factory()); it = result.first; } return *it->second.get(); diff --git a/src/dot.cpp b/src/dot.cpp index f5aa6a0d12e..c47318daf4b 100644 --- a/src/dot.cpp +++ b/src/dot.cpp @@ -105,7 +105,7 @@ DotRunner* DotManager::createRunner(const QCString &absDotName, const QCString& // we have a match if (md5Hash != runit->second->getMd5Hash()) { - err("md5 hash does not match for two different runs of %s !\n", qPrint(absDotName)); + err("md5 hash does not match for two different runs of {} !\n", absDotName); } rv = runit->second.get(); } @@ -137,7 +137,7 @@ bool DotManager::run() } else { - msg("Generating dot graphs using %d parallel threads...\n",Config_getInt(DOT_NUM_THREADS)); + msg("Generating dot graphs using {:d} parallel threads...\n",Config_getInt(DOT_NUM_THREADS)); } } size_t i=1; @@ -169,7 +169,7 @@ bool DotManager::run() { for (auto & dr : m_runners) { - msg("Running dot for graph %zu/%zu\n",prev,numDotRuns); + msg("Running dot for graph {}/{}\n",prev,numDotRuns); dr.second->run(); prev++; } @@ -189,7 +189,7 @@ bool DotManager::run() for (auto &f : results) { f.get(); - msg("Running dot for graph %zu/%zu\n",prev,numDotRuns); + msg("Running dot for graph {}/{}\n",prev,numDotRuns); prev++; } } @@ -208,7 +208,7 @@ bool DotManager::run() { if (fp.second.isSVGFile()) { - msg("Patching output file %zu/%zu\n",i,numFilePatchers); + msg("Patching output file {}/{}\n",i,numFilePatchers); if (!fp.second.run()) return FALSE; i++; } @@ -217,7 +217,7 @@ bool DotManager::run() { if (!fp.second.isSVGFile()) { - msg("Patching output file %zu/%zu\n",i,numFilePatchers); + msg("Patching output file {}/{}\n",i,numFilePatchers); if (!fp.second.run()) return FALSE; i++; } @@ -234,20 +234,20 @@ void writeDotGraphFromFile(const QCString &inFile,const QCString &outDir, Dir d(outDir.str()); if (!d.exists()) { - term("Output dir %s does not exist!\n",qPrint(outDir)); + term("Output dir {} does not exist!\n",outDir); } QCString imgExt = getDotImageExtension(); - QCString imgName = QCString(outFile)+"."+imgExt; - QCString absImgName = QCString(d.absPath())+"/"+imgName; - QCString absOutFile = QCString(d.absPath())+"/"+outFile; + QCString imgName = outFile+"."+imgExt; + QCString absImgName = d.absPath()+"/"+imgName; + QCString absOutFile = d.absPath()+"/"+outFile; DotRunner dotRun(inFile); - if (format==GOF_BITMAP) + if (format==GraphOutputFormat::BITMAP) { dotRun.addJob(Config_getEnumAsString(DOT_IMAGE_FORMAT),absImgName,srcFile,srcLine); } - else // format==GOF_EPS + else // format==GraphOutputFormat::EPS { if (Config_getBool(USE_PDFLATEX)) { @@ -279,24 +279,25 @@ void writeDotGraphFromFile(const QCString &inFile,const QCString &outDir, * \param graphId a unique id for this graph, use for dynamic sections * \param srcFile the source file * \param srcLine the line number in the source file + * \param newFile signal whether or not the file has been generated before (value `false`) or not. */ void writeDotImageMapFromFile(TextStream &t, const QCString &inFile, const QCString &outDir, const QCString &relPath, const QCString &baseName, const QCString &context,int graphId, - const QCString &srcFile,int srcLine) + const QCString &srcFile,int srcLine, bool newFile) { Dir d(outDir.str()); if (!d.exists()) { - term("Output dir %s does not exist!\n",qPrint(outDir)); + term("Output dir {} does not exist!\n",outDir); } QCString mapName = baseName+".map"; QCString imgExt = getDotImageExtension(); QCString imgName = baseName+"."+imgExt; - QCString absOutFile = QCString(d.absPath())+"/"+mapName; + QCString absOutFile = d.absPath()+"/"+mapName; DotRunner dotRun(inFile); dotRun.addJob(MAP_CMD,absOutFile,srcFile,srcLine); @@ -310,9 +311,12 @@ void writeDotImageMapFromFile(TextStream &t, { QCString svgName = outDir+"/"+baseName+".svg"; DotFilePatcher::writeSVGFigureLink(t,relPath,baseName,svgName); - DotFilePatcher patcher(svgName); - patcher.addSVGConversion("",TRUE,context,TRUE,graphId); - patcher.run(); + if (newFile) + { + DotFilePatcher patcher(svgName); + patcher.addSVGConversion("",TRUE,context,TRUE,graphId); + patcher.run(); + } } else // bitmap graphics { diff --git a/src/dot.h b/src/dot.h index 7e6189cd81e..4a13f577e19 100644 --- a/src/dot.h +++ b/src/dot.h @@ -56,6 +56,6 @@ void writeDotImageMapFromFile(TextStream &t, const QCString &inFile, const QCString& outDir, const QCString &relPath,const QCString& baseName, const QCString &context,int graphId, - const QCString &srcFile,int srcLine); + const QCString &srcFile,int srcLine,bool newFile); #endif diff --git a/src/dotcallgraph.cpp b/src/dotcallgraph.cpp index 4c18c8584fd..e80f27632a2 100644 --- a/src/dotcallgraph.cpp +++ b/src/dotcallgraph.cpp @@ -69,7 +69,7 @@ void DotCallGraph::buildGraph(DotNode *n,const MemberDef *md,int distance) n->addChild(bn,EdgeInfo::Blue,EdgeInfo::Solid); bn->addParent(n); bn->setDistance(distance); - m_usedNodes.insert(std::make_pair(uniqueId.str(),bn)); + m_usedNodes.emplace(uniqueId.str(),bn); buildGraph(bn,rmd,distance+1); } @@ -140,7 +140,7 @@ DotCallGraph::DotCallGraph(const MemberDef *md,bool inverse) TRUE // root node ); m_startNode->setDistance(0); - m_usedNodes.insert(std::make_pair(uniqueId.str(),m_startNode)); + m_usedNodes.emplace(uniqueId.str(),m_startNode); buildGraph(m_startNode,md,1); int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES); @@ -166,7 +166,7 @@ void DotCallGraph::computeTheGraph() { computeGraph( m_startNode, - CallGraph, + GraphType::CallGraph, m_graphFormat, m_inverse ? "RL" : "LR", FALSE, @@ -189,7 +189,7 @@ QCString DotCallGraph::writeGraph( const QCString &relPath,bool generateImageMap, int graphId) { - m_doNotAddImageToIndex = textFormat!=EOF_Html; + m_doNotAddImageToIndex = textFormat!=EmbeddedOutputFormat::Html; return DotGraph::writeGraph(out, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId); } diff --git a/src/dotclassgraph.cpp b/src/dotclassgraph.cpp index 78599f7e570..5c5908b3189 100644 --- a/src/dotclassgraph.cpp +++ b/src/dotclassgraph.cpp @@ -103,7 +103,7 @@ void DotClassGraph::addClass(const ClassDef *cd,DotNode *n,EdgeInfo::Colors colo n->addParent(bn); } bn->setDistance(distance); - m_usedNodes.insert(std::make_pair(fullName.str(),bn)); + m_usedNodes.emplace(fullName.str(),bn); //printf(" add new child node '%s' to %s hidden=%d url=%s\n", // qPrint(className),qPrint(n->label()),cd->isHidden(),qPrint(tmp_url)); @@ -231,17 +231,20 @@ static QCString joinLabels(const StringSet &ss) { QCString label; int count=1; - int maxLabels=10; + int maxLabels = Config_getInt(UML_MAX_EDGE_LABELS); auto it = std::begin(ss), e = std::end(ss); if (it!=e) // set not empty { - label += (*it++).c_str(); - for (; it!=e && count < maxLabels ; ++it,++count) + label = *it++; + for (; it!=e && (maxLabels==0 || countname()),distance,base); // ---- Add inheritance relations - if (m_graphType == Inheritance || m_graphType==Collaboration) + if (m_graphType == GraphType::Inheritance || m_graphType==GraphType::Collaboration) { for (const auto &bcd : base ? cd->baseClasses() : cd->subClasses()) { @@ -261,7 +264,7 @@ void DotClassGraph::buildGraph(const ClassDef *cd,DotNode *n,bool base,int dista addClass(bcd.classDef,n,EdgeInfo::protectionToColor(bcd.prot),QCString(),bcd.usedName,bcd.templSpecifiers,base,distance); } } - if (m_graphType == Collaboration) + if (m_graphType == GraphType::Collaboration) { // ---- Add usage relations @@ -333,15 +336,15 @@ DotClassGraph::DotClassGraph(const ClassDef *cd,GraphType t) cd ); m_startNode->setDistance(0); - m_usedNodes.insert(std::make_pair(className.str(),m_startNode)); + m_usedNodes.emplace(className.str(),m_startNode); buildGraph(cd,m_startNode,TRUE,1); - if (t==Inheritance) buildGraph(cd,m_startNode,FALSE,1); + if (t==GraphType::Inheritance) buildGraph(cd,m_startNode,FALSE,1); - m_lrRank = determineVisibleNodes(m_startNode,Config_getInt(DOT_GRAPH_MAX_NODES),t==Inheritance); + m_lrRank = determineVisibleNodes(m_startNode,Config_getInt(DOT_GRAPH_MAX_NODES),t==GraphType::Inheritance); DotNodeDeque openNodeQueue; openNodeQueue.push_back(m_startNode); - determineTruncatedNodes(openNodeQueue,t==Inheritance); + determineTruncatedNodes(openNodeQueue,t==GraphType::Inheritance); m_collabFileName = cd->collaborationGraphFileName(); m_inheritFileName = cd->inheritanceGraphFileName(); @@ -349,7 +352,7 @@ DotClassGraph::DotClassGraph(const ClassDef *cd,GraphType t) bool DotClassGraph::isTrivial() const { - if (m_graphType==Inheritance) + if (m_graphType==GraphType::Inheritance) return m_startNode->children().empty() && m_startNode->parents().empty(); else return !Config_getBool(UML_LOOK) && m_startNode->children().empty(); @@ -364,7 +367,7 @@ int DotClassGraph::numNodes() const { size_t numNodes = 0; numNodes+= m_startNode->children().size(); - if (m_graphType==Inheritance) + if (m_graphType==GraphType::Inheritance) { numNodes+= m_startNode->parents().size(); } @@ -380,10 +383,10 @@ QCString DotClassGraph::getBaseName() const { switch (m_graphType) { - case Collaboration: + case GraphType::Collaboration: return m_collabFileName; break; - case Inheritance: + case GraphType::Inheritance: return m_inheritFileName; break; default: @@ -400,7 +403,7 @@ void DotClassGraph::computeTheGraph() m_graphType, m_graphFormat, m_lrRank ? "LR" : "", - m_graphType == Inheritance, + m_graphType == GraphType::Inheritance, TRUE, m_startNode->label(), m_theGraph @@ -412,10 +415,10 @@ QCString DotClassGraph::getMapLabel() const QCString mapName; switch (m_graphType) { - case Collaboration: + case GraphType::Collaboration: mapName="coll_map"; break; - case Inheritance: + case GraphType::Inheritance: mapName="inherit_map"; break; default: @@ -430,10 +433,10 @@ QCString DotClassGraph::getImgAltText() const { switch (m_graphType) { - case Collaboration: + case GraphType::Collaboration: return "Collaboration graph"; break; - case Inheritance: + case GraphType::Inheritance: return "Inheritance graph"; break; default: diff --git a/src/dotdirdeps.cpp b/src/dotdirdeps.cpp index cf1c59682f4..74095c89980 100644 --- a/src/dotdirdeps.cpp +++ b/src/dotdirdeps.cpp @@ -52,7 +52,7 @@ class DotDirPropertyBuilder DotDirPropertyBuilder &makeTruncated (bool b=true) { m_property.isTruncated =b; return *this; } DotDirPropertyBuilder &makeOriginal (bool b=true) { m_property.isOriginal =b; return *this; } DotDirPropertyBuilder &makePeripheral(bool b=true) { m_property.isPeripheral=b; return *this; } - operator DotDirProperty() { return std::move(m_property); } + operator DotDirProperty() { return m_property; } private: DotDirProperty m_property; }; @@ -161,7 +161,7 @@ static void drawDirectory(TextStream &t, const DirDef *const directory, const Do "color=\"" << getDirectoryBorderColor(property) << "\", "; common_attributes(t, directory, property) << "];\n"; - directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory)); + directoriesInGraph.emplace(directory->getOutputFileBase().str(), directory); } /** Checks, if the directory is a the maximum drawn directory level. */ @@ -197,7 +197,7 @@ static void drawClusterOpening(TextStream &outputStream, const DirDef *const dir outputStream << " " << directory->getOutputFileBase() << " [shape=plaintext, " "label=\"" << DotNode::convertLabel(directory->shortName()) << "\"" "];\n"; - directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory)); + directoriesInGraph.emplace(directory->getOutputFileBase().str(), directory); } } @@ -222,9 +222,9 @@ static void addDependencies(DirRelations &dependencies,const DirDef *const srcDi QCString relationName; relationName.sprintf("dir_%06d_%06d", srcDir->dirIndex(), dstDir->dirIndex()); bool directRelation = isLeaf ? usedDirectory->hasDirectDstDeps() : usedDirectory->hasDirectDeps(); - auto &&dependency = std::make_unique(relationName, srcDir, usedDirectory.get()); - auto &&pair = std::make_pair(std::move(dependency),directRelation); - dependencies.emplace_back(std::move(pair)); + dependencies.emplace_back( + std::make_unique(relationName, srcDir, usedDirectory.get()), + directRelation); } } } @@ -288,7 +288,7 @@ void writeDotDirDepGraph(TextStream &t,const DirDef *dd,bool linkRelations) { DirDefMap dirsInGraph; - dirsInGraph.insert(std::make_pair(dd->getOutputFileBase().str(),dd)); + dirsInGraph.emplace(dd->getOutputFileBase().str(),dd); std::vector usedDirsNotDrawn, usedDirsDrawn; for (const auto& usedDir : dd->usedDirs()) diff --git a/src/dotfilepatcher.cpp b/src/dotfilepatcher.cpp index df6555a713c..20361099fe9 100644 --- a/src/dotfilepatcher.cpp +++ b/src/dotfilepatcher.cpp @@ -24,10 +24,18 @@ #include "dot.h" #include "dir.h" #include "portable.h" +#include "stringutil.h" // top part of the interactive SVG header -static const char svgZoomHeader1[] = R"svg( +static const char svgZoomHeader0[] = R"svg( +)svg"; + +static const char svgZoomHeader0_noinit[] = R"svg( + +)svg"; + +static const char svgZoomHeader1[] = R"svg(