├── .clang-format ├── .clusterfuzzlite ├── Dockerfile ├── build.sh └── project.yaml ├── .dockerignore ├── .git-blame-ignore-revs ├── .github ├── codecov.yml └── workflows │ ├── arch.yml │ ├── checks.yml │ ├── coverage.yml │ ├── docs.yml │ ├── fuzz-batch.yml │ ├── fuzz-continuous.yml │ ├── fuzz-cron.yml │ ├── fuzz-pr.yml │ ├── linux.yml │ ├── lite.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── benchmark ├── CMakeLists.txt ├── binarysize │ ├── CMakeLists.txt │ ├── binarysize_bench.py │ ├── graph-debug.png │ ├── graph-minsizerel.png │ ├── graph-release.png │ ├── run_binarysize_bench.py │ ├── template-control.cpp │ ├── template-iostream.cpp │ ├── template-scanf.cpp │ └── template-scnlib.cpp ├── buildtime │ ├── CMakeLists.txt │ ├── cstdio.cpp │ ├── empty.cpp │ ├── iostream.cpp │ ├── run-buildtime-benchmarks.sh │ └── scnlib.cpp └── runtime │ ├── CMakeLists.txt │ ├── basic │ ├── CMakeLists.txt │ └── basic_bench.cpp │ ├── common │ ├── bench_helpers.h │ └── benchmark_common.h │ ├── float │ ├── CMakeLists.txt │ ├── float_bench.cpp │ ├── float_bench.h │ ├── repeated.cpp │ └── single.cpp │ ├── integer │ ├── CMakeLists.txt │ ├── int_bench.cpp │ ├── int_bench.h │ ├── repeated.cpp │ └── single.cpp │ ├── results │ ├── float.png │ ├── int.png │ └── string.png │ └── string │ ├── CMakeLists.txt │ ├── lipsum.txt │ ├── string_bench.cpp │ ├── string_bench.h │ └── unicode.txt ├── cmake ├── Findpoxy.cmake ├── buildflags.cmake ├── charconv_compile_test.cpp ├── dependencies.cmake ├── icm_build_failure_parse_and_run.cmake ├── icm_build_failure_testing.cmake ├── install.cmake ├── options.cmake ├── sanitizers.cmake ├── scn-config.cmake.in └── util.cmake ├── docs ├── CMakeLists.txt ├── pages │ ├── faq.md │ ├── guide.md │ └── mainpage.md ├── poxy.toml ├── requirements.txt └── script │ └── monospace-headers.js ├── examples ├── CMakeLists.txt ├── example_1.cpp ├── example_2.cpp ├── example_3.cpp ├── example_4.cpp └── example_5.cpp ├── include └── scn │ ├── chrono.h │ ├── fwd.h │ ├── istream.h │ ├── ranges.h │ ├── regex.h │ ├── scan.h │ └── xchar.h ├── scripts ├── CMakeLists.txt ├── coverage.sh └── version.py ├── src └── scn │ ├── impl.cpp │ └── impl.h └── tests ├── CMakeLists.txt ├── clang-tidy ├── .clang-tidy └── main.cpp ├── consumer-test ├── CMakeLists.txt └── main.cpp ├── fuzz ├── CMakeLists.txt ├── chrono_fuzz.cpp ├── dictionaries │ ├── bool.txt │ ├── char.txt │ ├── chrono.txt │ ├── float.txt │ ├── format.txt │ ├── int.txt │ └── string.txt ├── float_fuzz.cpp ├── format_fuzz.cpp ├── fuzz.h ├── int_fuzz.cpp ├── run-fuzz.sh ├── seed-corpora │ ├── bool │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ └── 4 │ ├── char │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ └── 4 │ ├── chrono │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ ├── 4 │ │ ├── 5 │ │ └── 6 │ ├── float │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ ├── 4 │ │ ├── 5 │ │ ├── 6 │ │ ├── 7 │ │ ├── 8 │ │ ├── 9 │ │ ├── 10 │ │ ├── 11 │ │ ├── 12 │ │ ├── 13 │ │ ├── 14 │ │ ├── 15 │ │ ├── 16 │ │ ├── 17 │ │ ├── 18 │ │ ├── 19 │ │ └── 20 │ ├── format │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ ├── 4 │ │ ├── 5 │ │ ├── 6 │ │ ├── 7 │ │ ├── 8 │ │ ├── 9 │ │ ├── 10 │ │ ├── 11 │ │ ├── 12 │ │ ├── 13 │ │ ├── 14 │ │ ├── 15 │ │ ├── 16 │ │ ├── 17 │ │ ├── 18 │ │ ├── 19 │ │ └── 20 │ ├── int │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ ├── 4 │ │ ├── 5 │ │ ├── 6 │ │ ├── 7 │ │ ├── 8 │ │ ├── 9 │ │ ├── 10 │ │ ├── 11 │ │ ├── 12 │ │ ├── 13 │ │ ├── 14 │ │ ├── 15 │ │ ├── 16 │ │ ├── 17 │ │ ├── 18 │ │ ├── 19 │ │ ├── 20 │ │ ├── 21 │ │ ├── 22 │ │ ├── 23 │ │ ├── 24 │ │ ├── 25 │ │ ├── 26 │ │ ├── 27 │ │ ├── 28 │ │ └── 29 │ └── string │ │ ├── 1 │ │ ├── 2 │ │ ├── 3 │ │ ├── 4 │ │ ├── 5 │ │ └── 6 ├── string_fuzz.cpp └── string_impl_fuzz.cpp └── unittests ├── CMakeLists.txt ├── align_and_fill_test.cpp ├── args_test.cpp ├── buffer_test.cpp ├── char_test.cpp ├── chrono_test.cpp ├── compilefail_tests ├── CMakeLists.txt ├── brace_as_fill_character.cpp ├── charset_empty.cpp ├── charset_reversed_range.cpp ├── charset_unterminated.cpp ├── integer_with_string_presentation.cpp ├── invalid_unicode_in_format_string.cpp ├── letters_in_argument_id.cpp ├── locale_flag_with_locale_disabled.cpp ├── locale_flag_with_string.cpp ├── negative_argument_id.cpp ├── regex_disabled.cpp ├── regex_empty.cpp ├── regex_flag_invalid.cpp ├── regex_flag_multiple.cpp ├── regex_matches_non_contiguous_source.cpp ├── regex_no_presentation.cpp ├── regex_non_contiguous_source.cpp ├── regex_unterminated.cpp ├── regex_wide_strings.cpp ├── string_view_non_contiguous_source.cpp ├── unterminated_argument_id.cpp ├── unterminated_format_specifier.cpp └── usertype_non_contiguous_source.cpp ├── context_test.cpp ├── custom_type_test.cpp ├── error_test.cpp ├── examples_test_runner.py ├── float_test.cpp ├── format_string_parser_test.cpp ├── format_string_test.cpp ├── impl_tests ├── bits_test.cpp ├── bool_reader_test.cpp ├── contiguous_range_factory_test.cpp ├── find_fast_test.cpp ├── float_reader_test.cpp ├── function_ref_test.cpp ├── integer_reader_test.h ├── integer_reader_test.impl_narrow_classic.cpp ├── integer_reader_test.impl_narrow_localized.cpp ├── integer_reader_test.impl_wide_classic.cpp ├── integer_reader_test.impl_wide_localized.cpp ├── read_algorithms_test.cpp ├── reader_test_common.h ├── string_reader_test.cpp ├── text_width_test.cpp ├── transcode_test.cpp └── whitespace_skip_test.cpp ├── input_map_test.cpp ├── integer_test.cpp ├── istream_scanner_test.cpp ├── localized_tests └── localized_chrono_test.cpp ├── main.cpp ├── memory_test.cpp ├── ranges_test.cpp ├── regex_test.cpp ├── result_test.cpp ├── scan_test.cpp ├── source_test.cpp ├── standalone_fwd_include_test.cpp ├── standalone_scan_include_test.cpp ├── stdin_test.cpp ├── stdin_test_input.txt ├── stdin_test_runner.py ├── string_test.cpp ├── string_view_test.cpp ├── test_common.h ├── unicode_test.cpp └── wrapped_gtest.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | AccessModifierOffset: -4 3 | AlignAfterOpenBracket: true 4 | AlignConsecutiveMacros: AcrossEmptyLinesAndComments 5 | AllowShortBlocksOnASingleLine: Empty 6 | AllowShortFunctionsOnASingleLine: Empty 7 | BinPackArguments: true 8 | BinPackParameters: false 9 | BreakBeforeBraces: Stroustrup 10 | BreakConstructorInitializers: BeforeColon 11 | ColumnLimit: 80 12 | CommentPragmas: '^ IWYU pragma:' 13 | Cpp11BracedListStyle: true 14 | IndentWidth: 4 15 | Language: Cpp 16 | NamespaceIndentation: None 17 | PackConstructorInitializers: NextLine 18 | SpaceBeforeCpp11BracedList: false 19 | SpaceInEmptyParentheses: false 20 | Standard: Latest 21 | TabWidth: 4 22 | UseTab: Never 23 | -------------------------------------------------------------------------------- /.clusterfuzzlite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/oss-fuzz-base/base-builder 2 | 3 | RUN echo "CXX=$CXX" 4 | RUN echo "CXXFLAGS=$CXXFLAGS" 5 | 6 | RUN apt update && \ 7 | apt install -y --no-install-recommends \ 8 | ninja-build cmake && \ 9 | apt-get clean && \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | # Using re2 instead of std::regex to not time out due to too complex regular expressions 13 | # re2 needs abseil 14 | 15 | RUN git clone --depth 1 --branch 20240722.0 https://github.com/abseil/abseil-cpp $SRC/abseil-cpp && \ 16 | cd $SRC/abseil-cpp && \ 17 | mkdir build && cd build && \ 18 | cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && \ 19 | cmake --build . --target install && \ 20 | cd $SRC && rm -rf $SRC/abseil-cpp 21 | 22 | RUN git clone --depth 1 --branch 2024-07-02 https://github.com/google/re2 $SRC/re2 && \ 23 | cd $SRC/re2 && \ 24 | mkdir build && cd build && \ 25 | cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && \ 26 | cmake --build . --target install && \ 27 | cd $SRC && rm -rf $SRC/re2 28 | 29 | COPY . $SRC/scnlib 30 | WORKDIR $SRC/scnlib 31 | COPY ./.clusterfuzzlite/build.sh $SRC/ 32 | -------------------------------------------------------------------------------- /.clusterfuzzlite/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xeu 4 | 5 | mkdir build 6 | cd build 7 | 8 | cmake .. \ 9 | -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=17 \ 10 | -DSCN_DISABLE_TOP_PROJECT=ON -DSCN_FUZZING=ON \ 11 | -DSCN_FUZZING_LDFLAGS="$LIB_FUZZING_ENGINE" \ 12 | -DSCN_REGEX_BACKEND=re2 13 | 14 | cmake --build . 15 | 16 | # Binary targets 17 | cp tests/fuzz/scn_fuzz_* "$OUT" 18 | 19 | fuzz_src_dir="$(pwd)/../tests/fuzz" 20 | 21 | # Dictionaries and seed corpora 22 | copy_data() { 23 | cp "$fuzz_src_dir/dictionaries/$1.txt" "$OUT/scn_fuzz_$2.dict" 24 | 25 | zip "$OUT/scn_fuzz_$2_seed_corpus.zip" "$fuzz_src_dir"/seed-corpora/"$1"/* 26 | } 27 | copy_data float float 28 | copy_data format format 29 | copy_data int int 30 | copy_data string string 31 | copy_data string string_impl 32 | -------------------------------------------------------------------------------- /.clusterfuzzlite/project.yaml: -------------------------------------------------------------------------------- 1 | homepage: "https://github.com/eliaskosunen/scnlib" 2 | language: c++ 3 | primary_contact: "elias.kosunen@gmail.com" 4 | main_repo: "https://github.com/eliaskosunen/scnlib.git" 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | cmake-build*/ 3 | docs/html/ 4 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs 2 | # Change clang-format option NamespaceIndentation All -> None 3 | e27ab49d3eede2feef5392e1970d1a48a0a2984d 4 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "tests" 3 | - "include/scn/external" 4 | - "include/scn/util/expected_impl.h" 5 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | # TODO 2 | name: Checks 3 | 4 | on: 5 | #push: 6 | # branches: 7 | # - master 8 | # paths-ignore: 9 | # - '**.md' 10 | # - '**/docs/**' 11 | #pull_request: 12 | # branches: 13 | # - master 14 | # paths-ignore: 15 | # - '**.md' 16 | # - '**/docs/**' 17 | workflow_dispatch: 18 | 19 | jobs: 20 | clang-tidy: 21 | runs-on: ubuntu-22.04 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | std: [ 17, 20 ] 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - name: Run Clang-Tidy 32 | run: | 33 | #clang-tidy-14 tests/clang-tidy/main.cpp -warnings-as-errors=* -header-filter=.* -extra-arg=-std=c++${{ matrix.std }} -- 34 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/coverage.yml' 7 | - 'cmake/**' 8 | - 'include/**' 9 | - 'scripts/**' 10 | - 'src/**' 11 | - 'tests/**' 12 | - '**/CMakeLists.txt' 13 | pull_request: 14 | paths: 15 | - '.github/workflows/coverage.yml' 16 | - 'cmake/**' 17 | - 'include/**' 18 | - 'scripts/**' 19 | - 'src/**' 20 | - 'tests/**' 21 | - '**/CMakeLists.txt' 22 | workflow_dispatch: 23 | 24 | concurrency: 25 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 26 | cancel-in-progress: true 27 | 28 | env: 29 | APT_INSTALL: | 30 | sudo apt-get install 31 | -yq --no-install-suggests --no-install-recommends 32 | --allow-unauthenticated --allow-downgrades --allow-change-held-packages 33 | CCACHE_ABSSTDERR: true 34 | CCACHE_COMPRESS: true 35 | CCACHE_COMPRESSLEVEL: 6 36 | CCACHE_DIR: "/tmp/ccache" 37 | CCACHE_MAXSIZE: 50M 38 | CCACHE_NOHASHDIR: true 39 | CCACHE_SLOPPINESS: "pch_defines,time_macros" 40 | CCACHE_UNIFY: true 41 | DEBIAN_FRONTEND: noninteractive 42 | CXX: g++-11 43 | CMAKE_C_COMPILER_LAUNCHER: ccache 44 | CMAKE_CXX_COMPILER_LAUNCHER: ccache 45 | 46 | jobs: 47 | codecov: 48 | runs-on: ubuntu-22.04 49 | 50 | steps: 51 | - name: Setup CMake and Ninja 52 | uses: lukka/get-cmake@latest 53 | 54 | - name: Create Build Environment 55 | run: | 56 | sudo locale-gen en_US.UTF-8 57 | sudo locale-gen fi_FI.UTF-8 58 | sudo update-locale 59 | sudo locale -a 60 | 61 | sudo apt-get install -yq lcov ccache 62 | 63 | mkdir ${{ runner.workspace }}/build 64 | mkdir ${{ runner.workspace }}/report 65 | 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | 69 | - name: Load ccache 70 | uses: actions/cache@v4 71 | with: 72 | path: ${{ env.CCACHE_DIR }} 73 | key: ccache-coverage-${{ github.sha }} 74 | restore-keys: | 75 | ccache-coverage-${{ github.ref }} 76 | ccache-coverage-${{ github.base_ref }} 77 | ccache-coverage 78 | 79 | - name: Setup ccache 80 | run: | 81 | ccache --version 82 | ccache --zero-stats 83 | ccache --show-stats 84 | ccache --show-config 85 | 86 | - name: Run CMake 87 | working-directory: ${{ runner.workspace }}/build 88 | run: | 89 | cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \ 90 | -DCMAKE_CXX_STANDARD=20 \ 91 | -DSCN_TESTS_LOCALIZED=ON -DSCN_COVERAGE=ON \ 92 | $GITHUB_WORKSPACE 93 | 94 | - name: Build 95 | working-directory: ${{ runner.workspace }}/build 96 | run: | 97 | cmake --build . -- -k 0 98 | 99 | - name: Show ccache stats 100 | run: | 101 | ccache --show-stats 102 | 103 | - name: Generate Coverage Report 104 | working-directory: ${{ runner.workspace }}/build 105 | run: | 106 | ./coverage.sh 107 | cp coverage-filtered.info ${{ runner.workspace }}/report/coverage.info 108 | 109 | - name: Upload Coverage Report 110 | uses: codecov/codecov-action@v3 111 | with: 112 | directory: ${{ runner.workspace }}/report 113 | fail_ci_if_error: true 114 | verbose: true 115 | token: ${{ secrets.CODECOV_TOKEN }} 116 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - "**.h" 9 | - "**.hpp" 10 | - "**.dox" 11 | - "**.md" 12 | - "docs/**" 13 | - "**/docs.yml" 14 | - '.github/workflows/docs.yml' 15 | release: 16 | types: [published] 17 | workflow_dispatch: 18 | 19 | jobs: 20 | docs: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Install dependencies 25 | run: | 26 | sudo apt -y update 27 | sudo apt -y install --no-install-recommends git python3 python3-pip doxygen libgs-dev 28 | sudo -H pip3 install --upgrade poxy 29 | 30 | - name: Checkout 31 | uses: actions/checkout@v4 32 | with: 33 | fetch-depth: 0 34 | 35 | - name: Prepare repository (on push) 36 | if: ${{ github.event_name == 'push' }} 37 | run: | 38 | git fetch origin ${{ github.ref_name }}:refs/remotes/origin/${{ github.ref_name }} --tags --force 39 | git remote set-head origin -a 40 | git checkout ${{ github.ref_name }} 41 | git pull --force 42 | 43 | - name: Prepare repository (on release) 44 | if: ${{ github.event_name == 'release' }} 45 | run: | 46 | git fetch origin master:refs/remotes/origin/master --tags --force 47 | git remote set-head origin -a 48 | git checkout master 49 | git pull --force 50 | 51 | - name: Prepare repository (on workflow_dispatch) 52 | if: ${{ github.event_name == 'workflow_dispatch' }} 53 | run: | 54 | git fetch origin master:refs/remotes/origin/master --tags --force 55 | git remote set-head origin -a 56 | git checkout ${{ github.ref_name }} 57 | git pull --force 58 | 59 | - name: Generate docs 60 | run: | 61 | cd docs 62 | poxy --verbose --git-tags --min-version v2.0.0 63 | 64 | - name: Deploy 65 | uses: peaceiris/actions-gh-pages@v3 66 | with: 67 | github_token: ${{ secrets.GITHUB_TOKEN }} 68 | publish_dir: ./docs/html 69 | keep_files: true 70 | user_name: 'github-actions[bot]' 71 | user_email: 'github-actions[bot]@users.noreply.github.com' 72 | commit_message: Update documentation (GitHub actions deploy) 73 | -------------------------------------------------------------------------------- /.github/workflows/fuzz-batch.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite batch fuzzing 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0/6 * * *' # Every 6th hour 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | fuzz: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | sanitizer: 19 | - address 20 | - undefined 21 | #- memory 22 | 23 | steps: 24 | - name: Build fuzzers 25 | id: build 26 | uses: eliaskosunen/clusterfuzzlite/actions/build_fuzzers@main 27 | with: 28 | language: c++ 29 | sanitizer: ${{ matrix.sanitizer }} 30 | 31 | - name: Run fuzzers 32 | id: run 33 | uses: eliaskosunen/clusterfuzzlite/actions/run_fuzzers@main 34 | with: 35 | github-token: ${{ secrets.GITHUB_TOKEN }} 36 | fuzz-seconds: 1800 37 | mode: 'batch' 38 | sanitizer: ${{ matrix.sanitizer }} 39 | output-sarif: true 40 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 41 | -------------------------------------------------------------------------------- /.github/workflows/fuzz-continuous.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite continuous fuzzing 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - '.clusterfuzzlite/**' 9 | - '.github/workflows/fuzz-continuous.yml' 10 | - 'cmake/**' 11 | - 'include/**' 12 | - 'src/**' 13 | - 'tests/**' 14 | - '**/CMakeLists.txt' 15 | workflow_dispatch: 16 | 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | fuzz: 23 | runs-on: ubuntu-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | sanitizer: 28 | - address 29 | - undefined 30 | #- memory 31 | 32 | steps: 33 | - name: Build fuzzers 34 | id: build 35 | uses: eliaskosunen/clusterfuzzlite/actions/build_fuzzers@main 36 | with: 37 | language: c++ 38 | sanitizer: ${{ matrix.sanitizer }} 39 | upload-build: true 40 | 41 | - name: Run fuzzers 42 | id: run 43 | uses: eliaskosunen/clusterfuzzlite/actions/run_fuzzers@main 44 | with: 45 | github-token: ${{ secrets.GITHUB_TOKEN }} 46 | fuzz-seconds: 120 47 | mode: 'code-change' 48 | sanitizer: ${{ matrix.sanitizer }} 49 | output-sarif: true 50 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 51 | -------------------------------------------------------------------------------- /.github/workflows/fuzz-cron.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite cron-jobs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # Once a day at midnight 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | fuzz-prune: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Build fuzzers 18 | id: build 19 | uses: eliaskosunen/clusterfuzzlite/actions/build_fuzzers@main 20 | with: 21 | language: c++ 22 | 23 | - name: Run fuzzers 24 | id: run 25 | uses: eliaskosunen/clusterfuzzlite/actions/run_fuzzers@main 26 | with: 27 | github-token: ${{ secrets.GITHUB_TOKEN }} 28 | fuzz-seconds: 86400 29 | mode: 'prune' 30 | output-sarif: true 31 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 32 | 33 | fuzz-coverage: 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - name: Build fuzzers 38 | id: build 39 | uses: eliaskosunen/clusterfuzzlite/actions/build_fuzzers@main 40 | with: 41 | language: c++ 42 | sanitizer: coverage 43 | 44 | - name: Run fuzzers 45 | id: run 46 | uses: eliaskosunen/clusterfuzzlite/actions/run_fuzzers@main 47 | with: 48 | github-token: ${{ secrets.GITHUB_TOKEN }} 49 | fuzz-seconds: 600 50 | mode: 'coverage' 51 | sanitizer: 'coverage' 52 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 53 | -------------------------------------------------------------------------------- /.github/workflows/fuzz-pr.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite PR fuzzing 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.clusterfuzzlite/**' 7 | - '.github/workflows/fuzz-pr.yml' 8 | - 'cmake/**' 9 | - 'include/**' 10 | - 'src/**' 11 | - 'tests/**' 12 | - '**/CMakeLists.txt' 13 | workflow_dispatch: 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | fuzz: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | sanitizer: 26 | - address 27 | - undefined 28 | #- memory 29 | 30 | steps: 31 | - name: Build fuzzers 32 | id: build 33 | uses: eliaskosunen/clusterfuzzlite/actions/build_fuzzers@main 34 | with: 35 | language: c++ 36 | github-token: ${{ secrets.GITHUB_TOKEN }} 37 | sanitizer: ${{ matrix.sanitizer }} 38 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 39 | 40 | - name: Run fuzzers 41 | id: run 42 | uses: eliaskosunen/clusterfuzzlite/actions/run_fuzzers@main 43 | with: 44 | github-token: ${{ secrets.GITHUB_TOKEN }} 45 | fuzz-seconds: 600 46 | mode: 'code-change' 47 | sanitizer: ${{ matrix.sanitizer }} 48 | storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/eliaskosunen/scnlib-fuzz-corpus.git 49 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - v*/dev 8 | - ci/* 9 | - dev/* 10 | paths: 11 | - '.github/workflows/macos.yml' 12 | - 'benchmark/**' 13 | - 'cmake/**' 14 | - 'examples/**' 15 | - 'include/**' 16 | - 'src/**' 17 | - 'tests/unittests/**' 18 | - '**/CMakeLists.txt' 19 | pull_request: 20 | branches: 21 | - master 22 | - v*/dev 23 | - ci/* 24 | - dev/* 25 | paths: 26 | - '.github/workflows/macos.yml' 27 | - 'benchmark/**' 28 | - 'cmake/**' 29 | - 'examples/**' 30 | - 'include/**' 31 | - 'src/**' 32 | - 'tests/unittests/**' 33 | - '**/CMakeLists.txt' 34 | workflow_dispatch: 35 | 36 | concurrency: 37 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}-${{ github.event.schedule }} 38 | cancel-in-progress: true 39 | 40 | env: 41 | CCACHE_ABSSTDERR: true 42 | CCACHE_COMPRESS: true 43 | CCACHE_COMPRESSLEVEL: 6 44 | CCACHE_DIR: "/tmp/ccache" 45 | CCACHE_MAXSIZE: 50M 46 | CCACHE_NOHASHDIR: true 47 | CCACHE_SLOPPINESS: "pch_defines,time_macros" 48 | CCACHE_UNIFY: true 49 | 50 | jobs: 51 | build: 52 | runs-on: macos-${{ matrix.os }} 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | os: [ 14 ] 57 | cxx: [ brew-clang++, clang++, g++-14, g++-13, g++-12 ] 58 | std: [ 17 ] 59 | 60 | include: 61 | # brew-clang++ with later standards on macos 14 62 | - os: 14 63 | cxx: brew-clang++ 64 | std: 20 65 | - os: 14 66 | cxx: brew-clang++ 67 | std: 23 68 | 69 | # macos 15 with built-in clang++ and brew-clang++ 70 | - os: 15 71 | cxx: clang++ 72 | std: 17 73 | - os: 15 74 | cxx: brew-clang++ 75 | std: 17 76 | 77 | env: 78 | CXX: ${{ matrix.cxx }} 79 | OS: ${{ matrix.os }} 80 | CMAKE_C_COMPILER_LAUNCHER: ccache 81 | CMAKE_CXX_COMPILER_LAUNCHER: ccache 82 | 83 | steps: 84 | - name: Checkout 85 | uses: actions/checkout@v4 86 | 87 | - name: Create Build Environment 88 | run: | 89 | mkdir ${{ runner.workspace }}/build 90 | brew install --overwrite ccache 91 | 92 | - name: Setup Homebrew clang 93 | if: ${{ matrix.cxx == 'brew-clang++' }} 94 | run: | 95 | llvm_root="$(brew --prefix llvm)" 96 | echo "${llvm_root}/bin" >> $GITHUB_PATH 97 | echo "CXX=clang++" >> $GITHUB_ENV 98 | echo "LDFLAGS=-Wl,-rpath,${llvm_root}" >> $GITHUB_ENV 99 | echo "CPPFLAGS=-isystem ${llvm_root}/include" >> $GITHUB_ENV 100 | echo "CXXFLAGS=-isystem ${llvm_root}/include/c++/v1" >> $GITHUB_ENV 101 | 102 | - name: Load ccache 103 | uses: actions/cache@v4 104 | with: 105 | path: ${{ env.CCACHE_DIR }} 106 | key: ccache-${{ github.workflow }}-macos${{ matrix.os }}-${{ matrix.cxx }}-std${{ matrix.std }}-${{ github.sha }} 107 | restore-keys: | 108 | ccache-${{ github.workflow }}-macos${{ matrix.os }}-${{ matrix.cxx }}-std${{ matrix.std }}-${{ github.ref }} 109 | ccache-${{ github.workflow }}-macos${{ matrix.os }}-${{ matrix.cxx }}-std${{ matrix.std }}-${{ github.base_ref }} 110 | ccache-${{ github.workflow }}-macos${{ matrix.os }}-${{ matrix.cxx }}-std${{ matrix.std }} 111 | 112 | - name: Setup ccache 113 | run: | 114 | ccache --version 115 | ccache --zero-stats 116 | ccache --show-stats 117 | ccache --show-config 118 | 119 | - name: Run CMake 120 | working-directory: ${{ runner.workspace }}/build 121 | run: | 122 | cmake -DSCN_CI=ON -DCMAKE_CXX_STANDARD=${{ matrix.std }} $GITHUB_WORKSPACE 123 | 124 | - name: Build 125 | working-directory: ${{ runner.workspace }}/build 126 | run: cmake --build . --parallel -- -k 127 | 128 | - name: Show ccache stats 129 | run: | 130 | ccache --show-stats 131 | 132 | - name: Test 133 | working-directory: ${{ runner.workspace }}/build 134 | run: ctest --output-on-failure 135 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | docs/html 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project( 4 | scn 5 | VERSION 4.0.1 6 | DESCRIPTION "scanf for modern C++" 7 | HOMEPAGE_URL "https://scnlib.dev" 8 | LANGUAGES CXX 9 | ) 10 | 11 | option(SCN_DISABLE_TOP_PROJECT OFF) 12 | 13 | if (CMAKE_VERSION VERSION_LESS 3.21) 14 | get_property(not_top DIRECTORY PROPERTY PARENT_DIRECTORY) 15 | if (NOT not_top) 16 | set(PROJECT_IS_TOP_LEVEL ON) 17 | endif () 18 | endif () 19 | if (PROJECT_IS_TOP_LEVEL AND NOT SCN_DISABLE_TOP_PROJECT) 20 | set(SCN_IS_TOP_PROJECT ON) 21 | else () 22 | set(SCN_IS_TOP_PROJECT OFF) 23 | endif () 24 | 25 | if (SCN_IS_TOP_PROJECT AND NOT CMAKE_BUILD_TYPE) 26 | message(WARNING "No CMAKE_BUILD_TYPE set, defaulting to Release") 27 | set(CMAKE_BUILD_TYPE Release CACHE STRING "") 28 | endif() 29 | 30 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 31 | 32 | include(GNUInstallDirs) 33 | 34 | include(cmake/util.cmake) 35 | include(cmake/options.cmake) 36 | include(cmake/buildflags.cmake) 37 | include(cmake/dependencies.cmake) 38 | include(cmake/sanitizers.cmake) 39 | 40 | set(SCN_PUBLIC_HEADERS 41 | include/scn/fwd.h 42 | include/scn/scan.h 43 | include/scn/ranges.h 44 | include/scn/regex.h 45 | include/scn/istream.h 46 | include/scn/xchar.h 47 | ) 48 | set(SCN_PRIVATE_HEADERS 49 | src/scn/impl.h 50 | ) 51 | set(SCN_PRIVATE_SOURCES 52 | src/scn/impl.cpp 53 | ) 54 | 55 | add_library(scn 56 | "${SCN_PUBLIC_HEADERS}" 57 | "${SCN_PRIVATE_HEADERS}" 58 | "${SCN_PRIVATE_SOURCES}" 59 | ) 60 | target_include_directories(scn 61 | PUBLIC 62 | $ 63 | $ 64 | PRIVATE 65 | $ 66 | ) 67 | target_link_libraries(scn PRIVATE 68 | ${SCN_FAST_FLOAT_TARGET} 69 | ${SCN_REGEX_BACKEND_TARGET} 70 | ) 71 | set_library_flags(scn) 72 | 73 | set_property(TARGET scn PROPERTY SOVERSION 4) 74 | add_library(scn::scn ALIAS scn) 75 | 76 | add_library(scn_internal INTERFACE) 77 | target_link_libraries(scn_internal INTERFACE 78 | scn::scn 79 | ${SCN_FAST_FLOAT_TARGET} 80 | ${SCN_REGEX_BACKEND_TARGET} 81 | ) 82 | target_include_directories(scn_internal 83 | INTERFACE 84 | $ 85 | $ 86 | ) 87 | set_interface_flags(scn_internal) 88 | 89 | add_subdirectory(scripts) 90 | 91 | add_subdirectory(benchmark) 92 | add_subdirectory(docs) 93 | add_subdirectory(examples) 94 | 95 | enable_testing() 96 | add_subdirectory(tests) 97 | 98 | include(cmake/install.cmake) 99 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (SCN_BENCHMARKS) 2 | add_subdirectory(runtime) 3 | endif() 4 | 5 | if (SCN_BENCHMARKS_BINARYSIZE) 6 | add_subdirectory(binarysize) 7 | endif() 8 | 9 | if (SCN_BENCHMARKS_BUILDTIME) 10 | add_subdirectory(buildtime) 11 | endif() 12 | -------------------------------------------------------------------------------- /benchmark/binarysize/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(generate_binarysize_test_files template outdir sources) 2 | set(binarysize_test_path "${CMAKE_CURRENT_BINARY_DIR}/${outdir}/") 3 | set(binarysize_test_main "${binarysize_test_path}/binarysize_test_main.cpp") 4 | set(binarysize_test_header "${binarysize_test_path}/binarysize_test_all.h") 5 | 6 | file(WRITE "${binarysize_test_main}" "#include \"binarysize_test_all.h\"\n") 7 | file(APPEND "${binarysize_test_main}" "int main() {\n") 8 | 9 | set(source_files_list "${binarysize_test_main}" "${binarysize_test_header}") 10 | 11 | foreach (i RANGE 16) 12 | set(output_file "${binarysize_test_path}/binarysize_test_tmp_${i}.cpp") 13 | 14 | file(READ "${template}" template_contents) 15 | string(REPLACE "do_scan" "do_scan_${i}" template_contents "${template_contents}") 16 | string(REPLACE "42" "${i}" template_contents "${template_contents}") 17 | file(WRITE "${output_file}" "${template_contents}") 18 | 19 | list(APPEND source_files_list "${output_file}") 20 | 21 | file(APPEND "${binarysize_test_main}" "do_scan_${i}();\n") 22 | file(APPEND "${binarysize_test_header}" "void do_scan_${i}();\n") 23 | endforeach() 24 | 25 | file(APPEND "${binarysize_test_main}" "return 0;\n}\n") 26 | set(${sources} "${source_files_list}" PARENT_SCOPE) 27 | endfunction() 28 | 29 | add_library(scn_benchmark_binarysize_base INTERFACE) 30 | target_compile_options(scn_benchmark_binarysize_base INTERFACE 31 | $<$: 32 | -Wno-unused 33 | -Wno-unused-but-set-variable 34 | -Wno-redundant-decls 35 | -Wno-missing-declarations> 36 | $<$: 37 | -Wno-unused-variable 38 | -Wno-missing-prototypes>) 39 | disable_msvc_secure_flags(scn_benchmark_binarysize_base INTERFACE) 40 | 41 | # control 42 | generate_binarysize_test_files("${CMAKE_CURRENT_LIST_DIR}/template-control.cpp" out/control binarysize_control_sources) 43 | add_executable(scn_benchmark_binarysize_control "${binarysize_control_sources}") 44 | target_link_libraries(scn_benchmark_binarysize_control scn_benchmark_binarysize_base) 45 | 46 | # iostream 47 | generate_binarysize_test_files("${CMAKE_CURRENT_LIST_DIR}/template-iostream.cpp" out/iostream binarysize_iostream_sources) 48 | add_executable(scn_benchmark_binarysize_iostream "${binarysize_iostream_sources}") 49 | target_link_libraries(scn_benchmark_binarysize_iostream scn_benchmark_binarysize_base) 50 | 51 | # scanf 52 | generate_binarysize_test_files("${CMAKE_CURRENT_LIST_DIR}/template-scanf.cpp" out/scanf binarysize_scanf_sources) 53 | add_executable(scn_benchmark_binarysize_scanf "${binarysize_scanf_sources}") 54 | target_link_libraries(scn_benchmark_binarysize_scanf scn_benchmark_binarysize_base) 55 | 56 | # scnlib 57 | generate_binarysize_test_files("${CMAKE_CURRENT_LIST_DIR}/template-scnlib.cpp" out/scnlib binarysize_scnlib_sources) 58 | add_executable(scn_benchmark_binarysize_scnlib "${binarysize_scnlib_sources}") 59 | target_link_libraries(scn_benchmark_binarysize_scnlib scn_internal scn_benchmark_binarysize_base) 60 | 61 | add_custom_target(scn_benchmark_binarysize_prepare ALL 62 | COMMAND ${CMAKE_COMMAND} -E copy 63 | "${CMAKE_CURRENT_LIST_DIR}/binarysize_bench.py" 64 | "${CMAKE_CURRENT_LIST_DIR}/run_binarysize_bench.py" 65 | "${CMAKE_BINARY_DIR}/benchmark/binarysize" 66 | COMMENT "Copying binary size benchmark scripts") 67 | -------------------------------------------------------------------------------- /benchmark/binarysize/binarysize_bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | import sys 5 | import os 6 | import math 7 | import shutil 8 | 9 | 10 | def convert_size(size_bytes): 11 | if size_bytes == 0: 12 | return '0 B' 13 | size_name = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') 14 | i = int(math.floor(math.log(size_bytes, 1024))) 15 | p = math.pow(1024, i) 16 | s = round(size_bytes / p, 2) 17 | return f'{s} {size_name[i]}' 18 | 19 | 20 | def run(out): 21 | stripped = out + '.stripped' 22 | 23 | size_out = os.path.getsize(out) 24 | 25 | shutil.copyfile(out, stripped) 26 | subprocess.run(['strip', stripped]) 27 | 28 | size_stripped = os.path.getsize(stripped) 29 | 30 | os.remove(stripped) 31 | 32 | return size_out, size_stripped 33 | 34 | 35 | def main(): 36 | a, b = run(sys.argv[1]) 37 | print(f'{sys.argv[1]}:\nSize: {a} ({convert_size(a)})\nStripped: {b} ({convert_size(b)})') 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /benchmark/binarysize/graph-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/binarysize/graph-debug.png -------------------------------------------------------------------------------- /benchmark/binarysize/graph-minsizerel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/binarysize/graph-minsizerel.png -------------------------------------------------------------------------------- /benchmark/binarysize/graph-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/binarysize/graph-release.png -------------------------------------------------------------------------------- /benchmark/binarysize/run_binarysize_bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import pprint 6 | import matplotlib.pyplot as plt 7 | 8 | import binarysize_bench 9 | 10 | 11 | def rounded_kb(bytes): 12 | x = round(bytes / 1024, 1) 13 | if x >= 100: 14 | return int(x) 15 | return x 16 | 17 | 18 | def get_test_prettyname(test): 19 | names = { 20 | 'scn_benchmark_binarysize_control': 'empty', 21 | 'scn_benchmark_binarysize_scanf': '`std::scanf`', 22 | 'scn_benchmark_binarysize_iostream': '`std::istream`', 23 | 'scn_benchmark_binarysize_scnlib': '`scn::input`', 24 | } 25 | return names[test] 26 | 27 | 28 | def process_test(exec_dir, test): 29 | size, stripped = binarysize_bench.run(os.path.join(exec_dir, test)) 30 | return {'test': test, 'testPretty': get_test_prettyname(test).replace("`", ""), 31 | 'testPrettyMD': get_test_prettyname(test), 32 | 'size': size, 'stripped': stripped, 33 | 'sizeH': binarysize_bench.convert_size(size), 'strippedH': binarysize_bench.convert_size(stripped), 34 | 'sizeKiB': rounded_kb(size), 'strippedKiB': rounded_kb(stripped)} 35 | 36 | 37 | def make_plot(results, title): 38 | results = list(reversed(results)) 39 | 40 | names = list(map(lambda x: x['testPretty'].replace(' ', '\n'), results)) 41 | plt.figure(figsize=(10, 10.5)) 42 | plt.suptitle(f'Executable size benchmarks: {title}') 43 | 44 | a = plt.subplot(211) 45 | a.barh(names, list(map(lambda x: x['size'], results))) 46 | a.set_title('Executable size') 47 | plt.xlabel('Size in KiB') 48 | 49 | b = plt.subplot(212) 50 | b.barh(names, list(map(lambda x: x['stripped'], results))) 51 | b.set_title('Stripped size') 52 | plt.xlabel('Size in KiB') 53 | 54 | plt.show() 55 | 56 | 57 | def main(): 58 | exec_dir = sys.argv[1] 59 | title = sys.argv[2] 60 | pp = pprint.PrettyPrinter(indent=4) 61 | 62 | tests = [f for f 63 | in os.listdir(exec_dir) 64 | if os.path.isfile(os.path.join(exec_dir, f)) 65 | and f.startswith('scn_benchmark_binarysize_') 66 | and not f.endswith('stripped') 67 | and not f.endswith('.py')] 68 | presort_results = {item['test']: item for item in list(map(lambda test: process_test(exec_dir, test), tests))} 69 | results = [ 70 | presort_results['scn_benchmark_binarysize_control'], 71 | presort_results['scn_benchmark_binarysize_scanf'], 72 | presort_results['scn_benchmark_binarysize_iostream'], 73 | presort_results['scn_benchmark_binarysize_scnlib'], 74 | ] 75 | 76 | first_column_width = len(max(results, key=lambda x: len(x['testPrettyMD']))['testPrettyMD']) 77 | execsize_txt = 'Executable size' 78 | stripsize_txt = 'Stripped size' 79 | 80 | print('Full results pretty-printed') 81 | pp.pprint(results) 82 | print('\n') 83 | 84 | print('Formatted as markdown table (sorted by size, asc)') 85 | print(f'| {"Method":{first_column_width}} | {execsize_txt} | {stripsize_txt} |') 86 | print(f'| {":":-<{first_column_width}} | {":":->{len(execsize_txt)}} | {":":->{len(stripsize_txt)}} |') 87 | for result in results: 88 | print( 89 | f'| {result["testPrettyMD"]:{first_column_width}} | {result["sizeKiB"]:>{len(execsize_txt)}} | {result["strippedKiB"]:>{len(stripsize_txt)}} |') 90 | print('\n') 91 | 92 | make_plot(results, title) 93 | 94 | 95 | if __name__ == '__main__': 96 | main() 97 | -------------------------------------------------------------------------------- /benchmark/binarysize/template-control.cpp: -------------------------------------------------------------------------------- 1 | void do_scan() {} 2 | -------------------------------------------------------------------------------- /benchmark/binarysize/template-iostream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void do_scan() 4 | { 5 | int i; 6 | std::cin >> i; 7 | 8 | double d; 9 | std::cin >> d; 10 | 11 | long long ll; 12 | std::cin >> ll; 13 | 14 | std::string str; 15 | std::cin >> str; 16 | } 17 | -------------------------------------------------------------------------------- /benchmark/binarysize/template-scanf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void do_scan() 5 | { 6 | #ifdef __GNUC__ 7 | #pragma GCC diagnostic push 8 | #pragma GCC diagnostic ignored "-Wunused-result" 9 | #endif 10 | int i; 11 | scanf("%d", &i); 12 | 13 | double d; 14 | scanf("%lf", &d); 15 | 16 | long long ll; 17 | scanf("%lld", &ll); 18 | 19 | std::string str(13, '\0'); 20 | scanf("%12s", &str[0]); 21 | #ifdef __GNUC__ 22 | #pragma GCC diagnostic pop 23 | #endif 24 | } 25 | -------------------------------------------------------------------------------- /benchmark/binarysize/template-scnlib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void do_scan() 4 | { 5 | auto _1 = scn::input("{}"); 6 | auto _2 = scn::input("{}"); 7 | auto _3 = scn::input("{}"); 8 | auto _4 = scn::input("{}"); 9 | } 10 | -------------------------------------------------------------------------------- /benchmark/buildtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(scn_benchmark_buildtime_prepare ALL 2 | COMMAND ${CMAKE_COMMAND} -E copy 3 | "${CMAKE_CURRENT_LIST_DIR}/empty.cpp" 4 | "${CMAKE_CURRENT_LIST_DIR}/cstdio.cpp" 5 | "${CMAKE_CURRENT_LIST_DIR}/iostream.cpp" 6 | "${CMAKE_CURRENT_LIST_DIR}/scnlib.cpp" 7 | "${CMAKE_CURRENT_LIST_DIR}/run-buildtime-benchmarks.sh" 8 | "${CMAKE_BINARY_DIR}/benchmark/buildtime" 9 | COMMENT "Copying build time benchmark scripts") 10 | -------------------------------------------------------------------------------- /benchmark/buildtime/cstdio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | int i; 7 | scanf("%d", &i); 8 | 9 | double d; 10 | scanf("%lf", &d); 11 | 12 | long long ll; 13 | scanf("%lld", &ll); 14 | 15 | std::string str(13, '\0'); 16 | scanf("%12s", &str[0]); 17 | } 18 | -------------------------------------------------------------------------------- /benchmark/buildtime/empty.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /benchmark/buildtime/iostream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | int i; 6 | std::cin >> i; 7 | 8 | double d; 9 | std::cin >> d; 10 | 11 | long long ll; 12 | std::cin >> ll; 13 | 14 | std::string str; 15 | std::cin >> str; 16 | } 17 | -------------------------------------------------------------------------------- /benchmark/buildtime/run-buildtime-benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | run () { 4 | file=$1 5 | shift 6 | 7 | echo "$file" 8 | /usr/bin/time c++ -x c++ -I../include -L. "./benchmark/buildtime/${file}" -w "$@" 9 | } 10 | 11 | run_benchmarks () { 12 | run empty.cpp "$@" 13 | run cstdio.cpp "$@" 14 | run iostream.cpp "$@" 15 | run scnlib.cpp -lscn "$@" 16 | } 17 | 18 | echo "Debug" 19 | run_benchmarks -g 20 | 21 | echo "Release" 22 | run_benchmarks -O3 -DNDEBUG 23 | -------------------------------------------------------------------------------- /benchmark/buildtime/scnlib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | auto _1 = scn::input("{}"); 6 | auto _2 = scn::input("{}"); 7 | auto _3 = scn::input("{}"); 8 | auto _4 = scn::input("{}"); 9 | } 10 | -------------------------------------------------------------------------------- /benchmark/runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(scn_benchmark_runtime_common INTERFACE) 2 | target_include_directories(scn_benchmark_runtime_common INTERFACE common) 3 | target_compile_options(scn_benchmark_runtime_common INTERFACE 4 | $<$: 5 | -Wno-unused 6 | -Wno-redundant-decls> 7 | $<$: 8 | -Wno-unused-variable 9 | -Wno-used-but-marked-unused 10 | -Wno-gnu-zero-variadic-macro-arguments 11 | -Wno-global-constructors 12 | -Wno-exit-time-destructors 13 | -Wno-weak-vtables 14 | >) 15 | disable_msvc_secure_flags(scn_benchmark_runtime_common INTERFACE) 16 | 17 | function(scn_make_runtime_benchmark target) 18 | add_executable(${target} ${ARGN}) 19 | target_link_libraries(${target} PRIVATE 20 | scn_internal benchmark::benchmark benchmark::benchmark_main scn_benchmark_runtime_common) 21 | endfunction() 22 | 23 | add_subdirectory(basic) 24 | add_subdirectory(integer) 25 | add_subdirectory(float) 26 | add_subdirectory(string) 27 | 28 | -------------------------------------------------------------------------------- /benchmark/runtime/basic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | scn_make_runtime_benchmark(scn_basic_bench basic_bench.cpp) 2 | -------------------------------------------------------------------------------- /benchmark/runtime/common/benchmark_common.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #pragma once 19 | 20 | #include "scn/fwd.h" 21 | 22 | SCN_GCC_PUSH 23 | SCN_GCC_IGNORE("-Wsign-conversion") 24 | SCN_GCC_IGNORE("-Wswitch-default") 25 | SCN_GCC_IGNORE("-Wredundant-decls") 26 | 27 | SCN_CLANG_PUSH 28 | SCN_CLANG_IGNORE("-Wweak-vtables") 29 | SCN_CLANG_IGNORE("-Wglobal-constructors") 30 | SCN_CLANG_IGNORE("-Wzero-as-null-pointer-constant") 31 | SCN_CLANG_IGNORE("-Wshift-sign-overflow") 32 | 33 | #include 34 | 35 | SCN_CLANG_POP 36 | SCN_GCC_POP 37 | -------------------------------------------------------------------------------- /benchmark/runtime/float/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | scn_make_runtime_benchmark(scn_float_bench float_bench.cpp single.cpp repeated.cpp) 2 | -------------------------------------------------------------------------------- /benchmark/runtime/float/float_bench.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "benchmark_common.h" 19 | 20 | BENCHMARK_MAIN(); 21 | -------------------------------------------------------------------------------- /benchmark/runtime/integer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | scn_make_runtime_benchmark(scn_int_bench int_bench.cpp repeated.cpp single.cpp) 2 | -------------------------------------------------------------------------------- /benchmark/runtime/integer/int_bench.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "benchmark_common.h" 19 | 20 | BENCHMARK_MAIN(); 21 | -------------------------------------------------------------------------------- /benchmark/runtime/integer/int_bench.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | #include "bench_helpers.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | template 27 | std::vector make_integer_list(std::size_t n) 28 | { 29 | static std::uniform_int_distribution dist( 30 | std::numeric_limits::min(), std::numeric_limits::max()); 31 | std::vector result{}; 32 | for (size_t i = 0; i < n; ++i) { 33 | std::ostringstream oss; 34 | oss << dist(get_rng()); 35 | result.push_back(SCN_MOVE(oss.str())); 36 | } 37 | return result; 38 | } 39 | 40 | template 41 | const auto& get_integer_list() 42 | { 43 | static auto list = make_integer_list(2 << 12); 44 | return list; 45 | } 46 | 47 | template 48 | std::string make_integer_string(std::size_t n) 49 | { 50 | static std::uniform_int_distribution dist( 51 | std::numeric_limits::min(), std::numeric_limits::max()); 52 | 53 | std::ostringstream oss; 54 | for (size_t i = 0; i < n; ++i) { 55 | oss << dist(get_rng()) << " "; 56 | } 57 | return oss.str(); 58 | } 59 | 60 | template 61 | const std::string& get_integer_string() 62 | { 63 | static auto str = make_integer_string(2 << 12); 64 | return str; 65 | } 66 | 67 | inline int sscanf_integral(const char* ptr, int& i) 68 | { 69 | return std::sscanf(ptr, "%d", &i); 70 | } 71 | inline int sscanf_integral(const char* ptr, long long& i) 72 | { 73 | return std::sscanf(ptr, "%lld", &i); 74 | } 75 | inline int sscanf_integral(const char* ptr, unsigned& i) 76 | { 77 | return std::sscanf(ptr, "%u", &i); 78 | } 79 | 80 | inline int sscanf_integral_n(const char*& ptr, int& i) 81 | { 82 | int n{}; 83 | auto ret = std::sscanf(ptr, "%d%n", &i, &n); 84 | ptr += n + 1; 85 | return ret; 86 | } 87 | inline int sscanf_integral_n(const char*& ptr, long long& i) 88 | { 89 | int n{}; 90 | auto ret = std::sscanf(ptr, "%lld%n", &i, &n); 91 | ptr += n + 1; 92 | return ret; 93 | } 94 | inline int sscanf_integral_n(const char*& ptr, unsigned& i) 95 | { 96 | int n{}; 97 | auto ret = std::sscanf(ptr, "%u%n", &i, &n); 98 | ptr += n + 1; 99 | return ret; 100 | } 101 | 102 | inline bool strtol_integral(const char* ptr, int& i) 103 | { 104 | char* endptr{}; 105 | auto tmp = std::strtol(ptr, &endptr, 0); 106 | i = static_cast(tmp); 107 | return endptr != ptr; 108 | } 109 | inline bool strtol_integral(const char* ptr, long long& i) 110 | { 111 | char* endptr{}; 112 | i = std::strtoll(ptr, &endptr, 0); 113 | return endptr != ptr; 114 | } 115 | inline bool strtol_integral(const char* ptr, unsigned& i) 116 | { 117 | char* endptr{}; 118 | auto tmp = std::strtoul(ptr, &endptr, 0); 119 | i = static_cast(tmp); 120 | return endptr != ptr; 121 | } 122 | 123 | inline int strtol_integral_n(const char*& ptr, int& i) 124 | { 125 | char* endptr{}; 126 | auto tmp = std::strtol(ptr, &endptr, 0); 127 | if (*ptr == '\0') { 128 | return EOF; 129 | } 130 | if (endptr == ptr) { 131 | return 1; 132 | } 133 | ptr = endptr; 134 | i = static_cast(tmp); 135 | return 0; 136 | } 137 | inline int strtol_integral_n(const char*& ptr, long long& i) 138 | { 139 | char* endptr{}; 140 | i = std::strtoll(ptr, &endptr, 0); 141 | if (*ptr == '\0') { 142 | return EOF; 143 | } 144 | if (endptr == ptr) { 145 | return 1; 146 | } 147 | ptr = endptr; 148 | return 0; 149 | } 150 | inline int strtol_integral_n(const char*& ptr, unsigned& i) 151 | { 152 | char* endptr{}; 153 | auto tmp = std::strtoul(ptr, &endptr, 0); 154 | if (*ptr == '\0') { 155 | return EOF; 156 | } 157 | if (endptr == ptr) { 158 | return 1; 159 | } 160 | ptr = endptr; 161 | i = static_cast(tmp); 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /benchmark/runtime/results/float.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/runtime/results/float.png -------------------------------------------------------------------------------- /benchmark/runtime/results/int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/runtime/results/int.png -------------------------------------------------------------------------------- /benchmark/runtime/results/string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/benchmark/runtime/results/string.png -------------------------------------------------------------------------------- /benchmark/runtime/string/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | scn_make_runtime_benchmark(scn_string_bench string_bench.cpp) 2 | target_link_libraries(scn_string_bench PRIVATE scn_internal) 3 | 4 | add_custom_target(scn_string_bench_prepare ALL 5 | COMMAND ${CMAKE_COMMAND} -E copy 6 | "${CMAKE_CURRENT_LIST_DIR}/lipsum.txt" 7 | "${CMAKE_CURRENT_LIST_DIR}/unicode.txt" 8 | "${CMAKE_BINARY_DIR}/benchmark/runtime/string" 9 | COMMENT "Copying string benchmark input files") 10 | -------------------------------------------------------------------------------- /benchmark/runtime/string/string_bench.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "bench_helpers.h" 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | template 28 | std::basic_string make_benchmark_string(const std::string& sourcefile) 29 | { 30 | std::ifstream fstr{sourcefile}; 31 | std::stringstream ss; 32 | ss << fstr.rdbuf(); 33 | 34 | if constexpr (sizeof(CharT) == 1) { 35 | return ss.str(); 36 | } 37 | else { 38 | std::basic_string str; 39 | scn::impl::transcode_to_string(std::string_view{ss.str()}, str); 40 | return str; 41 | } 42 | } 43 | 44 | struct lipsum_tag {}; 45 | struct unicode_tag {}; 46 | 47 | template 48 | std::basic_string get_benchmark_input() 49 | { 50 | if (std::is_same_v) 51 | return make_benchmark_string("lipsum.txt"); 52 | return make_benchmark_string("unicode.txt"); 53 | } 54 | -------------------------------------------------------------------------------- /cmake/Findpoxy.cmake: -------------------------------------------------------------------------------- 1 | find_program(POXY_EXECUTABLE 2 | NAMES poxy 3 | DOC "Path to poxy executable") 4 | 5 | include(FindPackageHandleStandardArgs) 6 | find_package_handle_standard_args(poxy "Failed to find poxy executable" POXY_EXECUTABLE) 7 | -------------------------------------------------------------------------------- /cmake/charconv_compile_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | int main() { 23 | std::string_view input("3.14"); 24 | double value{}; 25 | auto [ptr, ec] = std::from_chars(input.data(), input.data() + input.size(), value); 26 | if (ec != std::errc{}) { 27 | std::fprintf(stderr, "std::from_chars failed with error code %d\n", static_cast(ec)); 28 | return 1; 29 | } 30 | if (ptr != input.data() + input.size()) { 31 | std::fprintf(stderr, "std::from_chars failed with invalid pointer: %p, expected %p\n", ptr, input.data() + input.size()); 32 | return 1; 33 | } 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /cmake/icm_build_failure_parse_and_run.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Borislav Stanimirov 2 | # SPDX-License-Identifier: MIT 3 | # 4 | 5 | # This file is bundled with icm_build_failure_testing.cmake 6 | # It is expected to be in the same directory. 7 | 8 | if(DEFINED CFG) 9 | # optional cfg 10 | # that way one can run the generated script from the command line as: 11 | # $ cmake -P script.cmake 12 | set(cfgArg "--config;${CFG}") 13 | endif() 14 | 15 | execute_process( 16 | COMMAND ${CMAKE_COMMAND} --build . --target @ARG_TARGET@ ${cfgArg} 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 18 | RESULT_VARIABLE res 19 | ERROR_VARIABLE out 20 | # pipe OUTPUT_VARIABLE as well 21 | # *some* compilers (MSVC) report errors to the standard output 22 | OUTPUT_VARIABLE out 23 | ) 24 | 25 | if(res EQUAL 0) 26 | # Build command didn't fail. This means the test fails 27 | message(FATAL_ERROR "Error: Building '@ARG_TARGET@' didn't fail") 28 | endif() 29 | 30 | # collect possible errors from source 31 | file(READ "@parsedSourcePath@" sourceText) 32 | string(REGEX MATCHALL "//[ ]*build error:[^\n]+" matchErrors ${sourceText}) 33 | 34 | # look for collected errors in output 35 | foreach(possibleError ${matchErrors}) 36 | string(REGEX MATCH "//[ ]*build error:[ \t]*(.+)$" _ "${possibleError}") 37 | set(possibleError "${CMAKE_MATCH_1}") 38 | string(FIND "${out}" "${possibleError}" pos) 39 | if(NOT pos EQUAL -1) 40 | message("Success: output when building '@ARG_TARGET@' contains '${possibleError}'") 41 | return() 42 | endif() 43 | 44 | # prepare an output-firendly string in case the test fails 45 | set(outErrors "${outErrors}\n${possibleError}") 46 | endforeach() 47 | 48 | # print execute_process output for debugging purposes 49 | message("${out}") 50 | # print error 51 | message(FATAL_ERROR "Error: Building '@ARG_TARGET@' failed, but output doesn't contain any of the expected errors:${outErrors}") 52 | -------------------------------------------------------------------------------- /cmake/install.cmake: -------------------------------------------------------------------------------- 1 | if (NOT SCN_INSTALL) 2 | return() 3 | endif() 4 | 5 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/scn) 6 | 7 | set(targets "scn") 8 | if (NOT SCN_DISABLE_FAST_FLOAT AND NOT SCN_USE_EXTERNAL_FAST_FLOAT) 9 | list(APPEND targets fast_float) 10 | endif () 11 | 12 | install(TARGETS ${targets} 13 | EXPORT scn-targets 14 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 15 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 16 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 17 | COMPONENT scnlib_Development 18 | ) 19 | 20 | install(DIRECTORY 21 | include/ 22 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 23 | COMPONENT scnlib_Development 24 | ) 25 | 26 | install(EXPORT scn-targets 27 | FILE 28 | scn-targets.cmake 29 | NAMESPACE 30 | scn:: 31 | DESTINATION 32 | "${INSTALL_CONFIGDIR}" 33 | COMPONENT 34 | scnlib_Development 35 | ) 36 | 37 | include(CMakePackageConfigHelpers) 38 | write_basic_package_version_file( 39 | "${CMAKE_CURRENT_BINARY_DIR}/scn-config-version.cmake" 40 | VERSION ${PROJECT_VERSION} 41 | COMPATIBILITY SameMajorVersion 42 | ) 43 | 44 | set(SCN_FIND_DEPENDENCIES "include(CMakeFindDependencyMacro)\n") 45 | set(SCN_ADDITIONAL_LIBRARIES "") 46 | if (NOT SCN_DISABLE_FAST_FLOAT AND SCN_USE_EXTERNAL_FAST_FLOAT) 47 | set(SCN_FIND_DEPENDENCIES "${SCN_FIND_DEPENDENCIES}\n find_dependency(FastFloat 5.3.0)") 48 | set(SCN_ADDITIONAL_LIBRARIES "${SCN_ADDITIONAL_LIBRARIES} FastFloat::fast_float") 49 | endif() 50 | 51 | configure_package_config_file( 52 | "${PROJECT_SOURCE_DIR}/cmake/scn-config.cmake.in" 53 | "${CMAKE_CURRENT_BINARY_DIR}/scn-config.cmake" 54 | INSTALL_DESTINATION "${INSTALL_CONFIGDIR}" 55 | ) 56 | 57 | install(FILES 58 | "${CMAKE_CURRENT_BINARY_DIR}/scn-config.cmake" 59 | "${CMAKE_CURRENT_BINARY_DIR}/scn-config-version.cmake" 60 | DESTINATION "${INSTALL_CONFIGDIR}" 61 | COMPONENT scnlib_Development 62 | ) 63 | 64 | export(EXPORT scn-targets 65 | FILE "${CMAKE_CURRENT_BINARY_DIR}/scn-targets.cmake" 66 | NAMESPACE scn:: 67 | ) 68 | 69 | export(PACKAGE scn) 70 | -------------------------------------------------------------------------------- /cmake/sanitizers.cmake: -------------------------------------------------------------------------------- 1 | add_library(scn_sanitizer_asan INTERFACE) 2 | if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) 3 | set(ASAN_FLAG -g -fsanitize=address -fno-omit-frame-pointer) 4 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 5 | set(ASAN_FLAG /fsanitize=address) 6 | else() 7 | set(ASAN_FLAG) 8 | endif () 9 | target_compile_options(scn_sanitizer_asan INTERFACE ${ASAN_FLAG}) 10 | target_link_options(scn_sanitizer_asan INTERFACE ${ASAN_FLAG}) 11 | 12 | add_library(scn_sanitizer_ubsan INTERFACE) 13 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 14 | set(UBSAN_FLAG -g -fsanitize=undefined) 15 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 16 | set(UBSAN_FLAG -g -fsanitize=undefined,float-divide-by-zero,integer,implicit-conversion,local-bounds,nullability) 17 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 18 | set(UBSAN_FLAG /fsanitize=undefined) 19 | else() 20 | set(UBSAN_FLAG) 21 | endif () 22 | target_compile_options(scn_sanitizer_ubsan INTERFACE ${UBSAN_FLAG}) 23 | target_link_options(scn_sanitizer_ubsan INTERFACE ${UBSAN_FLAG}) 24 | 25 | add_library(scn_sanitizer_msan INTERFACE) 26 | if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) 27 | set(MSAN_FLAG -g -fsanitize=memory) 28 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 29 | set(MSAN_FLAG /fsanitize=memory) 30 | else() 31 | set(MSAN_FLAG) 32 | endif () 33 | target_compile_options(scn_sanitizer_msan INTERFACE ${MSAN_FLAG}) 34 | target_link_options(scn_sanitizer_msan INTERFACE ${MSAN_FLAG}) 35 | 36 | add_library(scn_sanitizer_fuzzer INTERFACE) 37 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 38 | set(FUZZER_FLAG -g -fsanitize=fuzzer -fno-omit-frame-pointer) 39 | target_compile_options(scn_sanitizer_fuzzer INTERFACE ${FUZZER_FLAG}) 40 | target_link_options(scn_sanitizer_fuzzer INTERFACE ${FUZZER_FLAG}) 41 | endif() 42 | 43 | add_library(scn_sanitizer_stackprotect INTERFACE) 44 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 45 | set(STACKPROTECT_FLAG -g -fstack-protector-all -mshstk) 46 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 47 | set(STACKPROTECT_FLAG -g -fstack-protector-all -flto -fvisibility=hidden -fsanitize=cfi) 48 | if (SCN_USE_SAFESTACK) 49 | set(STACKPROTECT_FLAG ${STACKPROTECT_FLAG} -fsanitize=safe-stack) 50 | else() 51 | set(STACKPROTECT_FLAG ${STACKPROTECT_FLAG} -mshstk) 52 | endif() 53 | else() 54 | set(STACKPROTECT_FLAG) 55 | endif() 56 | target_compile_options(scn_sanitizer_stackprotect INTERFACE ${STACKPROTECT_FLAG}) 57 | target_link_options(scn_sanitizer_stackprotect INTERFACE ${STACKPROTECT_FLAG}) 58 | 59 | add_library(scn_sanitizers INTERFACE) 60 | 61 | if (SCN_USE_ASAN) 62 | target_link_libraries(scn_sanitizers INTERFACE scn_sanitizer_asan) 63 | endif() 64 | if (SCN_USE_UBSAN) 65 | target_link_libraries(scn_sanitizers INTERFACE scn_sanitizer_ubsan) 66 | endif() 67 | if (SCN_USE_MSAN) 68 | target_link_libraries(scn_sanitizers INTERFACE scn_sanitizer_msan) 69 | endif() 70 | if (SCN_USE_STACK_PROTECT) 71 | target_link_libraries(scn_sanitizers INTERFACE scn_sanitizer_stackprotect) 72 | endif() 73 | 74 | if (SCN_FUZZING) 75 | add_library(scn_fuzzer INTERFACE) 76 | target_link_libraries(scn_fuzzer INTERFACE scn_sanitizers scn_sanitizer_fuzzer) 77 | endif() 78 | -------------------------------------------------------------------------------- /cmake/scn-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | if (@SCN_USE_EXTERNAL_FAST_FLOAT@) 6 | find_dependency(FastFloat) 7 | endif () 8 | 9 | if ((@SCN_REGEX_BACKEND@ STREQUAL "Boost") AND @SCN_USE_EXTERNAL_REGEX_BACKEND@) 10 | find_dependency(Boost COMPONENTS regex) 11 | 12 | if (@SCN_REGEX_USE_ICU@) 13 | find_dependency(ICU COMPONENTS data i18n uc) 14 | endif () 15 | endif () 16 | 17 | if ((@SCN_REGEX_BACKEND@ STREQUAL "re2") AND @SCN_USE_EXTERNAL_REGEX_BACKEND@) 18 | find_dependency(re2) 19 | endif () 20 | 21 | check_required_components(scn) 22 | 23 | if (TARGET scn::scn) 24 | return() 25 | endif () 26 | 27 | include("${CMAKE_CURRENT_LIST_DIR}/scn-targets.cmake") 28 | -------------------------------------------------------------------------------- /cmake/util.cmake: -------------------------------------------------------------------------------- 1 | # Set policy if policy is available 2 | function(set_policy POL VAL) 3 | if(POLICY ${POL}) 4 | cmake_policy(SET ${POL} ${VAL}) 5 | endif() 6 | endfunction() 7 | 8 | # Define function "source_group_by_path with three mandatory arguments (PARENT_PATH, REGEX, GROUP, ...) 9 | # to group source files in folders (e.g. for MSVC solutions). 10 | # 11 | # Example: 12 | # source_group_by_path("${CMAKE_CURRENT_SOURCE_DIR}/src" "\\\\.h$|\\\\.inl$|\\\\.cpp$|\\\\.c$|\\\\.ui$|\\\\.qrc$" "Source Files" ${sources}) 13 | function(source_group_by_path PARENT_PATH REGEX GROUP) 14 | foreach (FILENAME ${ARGN}) 15 | get_filename_component(FILEPATH "${FILENAME}" REALPATH) 16 | file(RELATIVE_PATH FILEPATH ${PARENT_PATH} ${FILEPATH}) 17 | get_filename_component(FILEPATH "${FILEPATH}" DIRECTORY) 18 | 19 | string(REPLACE "/" "\\" FILEPATH "${FILEPATH}") 20 | 21 | source_group("${GROUP}\\${FILEPATH}" REGULAR_EXPRESSION "${REGEX}" FILES ${FILENAME}) 22 | endforeach() 23 | endfunction() 24 | 25 | # Function that extract entries matching a given regex from a list. 26 | # ${OUTPUT} will store the list of matching filenames. 27 | function(list_extract OUTPUT REGEX) 28 | foreach(FILENAME ${ARGN}) 29 | if(${FILENAME} MATCHES "${REGEX}") 30 | list(APPEND ${OUTPUT} ${FILENAME}) 31 | endif() 32 | endforeach() 33 | 34 | set(${OUTPUT} ${${OUTPUT}} PARENT_SCOPE) 35 | endfunction() 36 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT SCN_DOCS) 2 | return() 3 | endif() 4 | 5 | find_package(Doxygen QUIET) 6 | if (NOT DOXYGEN_FOUND) 7 | message(STATUS "Target 'scn_docs' disabled for scnlib (doxygen required)") 8 | return() 9 | endif() 10 | 11 | find_package(poxy QUIET) 12 | if (NOT POXY_FOUND) 13 | message(STATUS "Target 'scn_docs' disabled for scnlib (poxy required)") 14 | return() 15 | endif() 16 | 17 | set(SCN_ABSOLUTE_PUBLIC_HEADERS "${SCN_PUBLIC_HEADERS}") 18 | list(TRANSFORM SCN_ABSOLUTE_PUBLIC_HEADERS PREPEND "${CMAKE_CURRENT_LIST_DIR}/../") 19 | 20 | add_custom_command( 21 | COMMAND ${POXY_EXECUTABLE} 22 | WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" 23 | OUTPUT "${CMAKE_CURRENT_LIST_DIR}/html" 24 | MAIN_DEPENDENCY "${CMAKE_CURRENT_LIST_DIR}/poxy.toml" 25 | DEPENDS 26 | "${CMAKE_CURRENT_LIST_DIR}/pages/faq.md" 27 | "${CMAKE_CURRENT_LIST_DIR}/pages/guide.md" 28 | "${CMAKE_CURRENT_LIST_DIR}/pages/mainpage.md" 29 | "${CMAKE_CURRENT_LIST_DIR}/../CHANGELOG.md" 30 | "${CMAKE_CURRENT_LIST_DIR}/script/monospace-headers.js" 31 | "${SCN_ABSOLUTE_PUBLIC_HEADERS}" 32 | COMMENT "Generating docs" 33 | VERBATIM) 34 | add_custom_target(scn_docs DEPENDS "${CMAKE_CURRENT_LIST_DIR}/html") 35 | -------------------------------------------------------------------------------- /docs/pages/faq.md: -------------------------------------------------------------------------------- 1 | \page faq FAQ 2 | \tableofcontents 3 | 4 | \section faq-1 Why doesn't `scn::scan(input, "{},{}")` work like I expected it to work? 5 | 6 | The format string given to `scn::scan` is not a pattern, or a regular expression. 7 | Instead, not unlike `std::scanf`, the format string is evaluated left-to-right, 8 | and each replacement field (`{}`) is scanned based on the rules given 9 | by the type to be scanned, and the options inside the replacement field. 10 | 11 | For example, `scn::scan(input, "{},{}")` means: 12 | 1. scan a `string` with default options (empty replacement field `{}`), 13 | which means to scan until whitespace 14 | 2. scan a literal ',' character, and discard it 15 | 3. scan another `string`, again with default options 16 | 17 | Compare this with a call to `scanf`, with the same behavior 18 | (discounting buffer overflow prevention): 19 | `std::sscanf(input, "%s,%s", output1, output2)`. 20 | 21 | To reiterate, 22 | the way values are scanned is only influenced by the type of the value to be scanned, 23 | and the options given in the replacement field (inside the `{}`). 24 | The context around the replacement field in the format string is not considered. 25 | 26 | To scan until a ',', one can do: 27 | `scn::scan("{:[^,]},{}")`. 28 | The `[^,]` in the formatting options for the first argument modifies the scanning behavior to 29 | accept all characters except ','. Thus, it won't read until whitespace, 30 | but until a comma, after which the comma is consumed with the literal ',' in the format string. 31 | -------------------------------------------------------------------------------- /docs/poxy.toml: -------------------------------------------------------------------------------- 1 | name = "scnlib" 2 | author = "Elias Kosunen" 3 | description = "scanf for modern C++" 4 | cpp = 17 5 | github = "eliaskosunen/scnlib" 6 | license = ['Apache-2.0', 'https://github.com/eliaskosunen/scnlib/blob/master/LICENSE'] 7 | theme = "dark" 8 | show_includes = false 9 | navbar = ['namespaces', 'classes', 'modules', 'pages', 'repo'] 10 | changelog = true 11 | jquery = true 12 | scripts = [ 13 | 'script/monospace-headers.js' 14 | ] 15 | 16 | [warnings] 17 | enabled = true 18 | treat_as_errors = false 19 | undocumented = true 20 | 21 | [sources] 22 | paths = ['pages', '../include/scn'] 23 | patterns = ['*.h', '*.hpp', '*.md', '*.dox'] 24 | strip_paths = ['../include', 'pages'] 25 | #extract_all = true 26 | 27 | [macros] 28 | 'SCN_DOXYGEN' = '1' 29 | 'SCN_USE_IOSTREAMS' = '1' 30 | 'SCN_NOEXCEPT' = 'noexcept' 31 | 'SCN_NOEXCEPT_P(a)' = 'noexcept(a)' 32 | 'SCN_CONSTEVAL' = 'consteval' 33 | 'SCN_NODISCARD' = '[[nodiscard]]' 34 | 'SCN_MAYBE_UNUSED' = '[[maybe_unused]]' 35 | 'SCN_NO_UNIQUE_ADDRESS' = '[[no_unique_address]]' 36 | 'SCN_MOVE(x)' = 'std::move(x)' 37 | 'SCN_FWD(x)' = 'std::forward(x)' 38 | 'SCN_DECLVAL(x)' = 'std::declval()' 39 | 'SCN_BEGIN_NAMESPACE' = '' 40 | 'SCN_END_NAMESPACE' = '' 41 | 'SCN_GCC_PUSH' = '' 42 | 'SCN_GCC_IGNORE(...)' = '' 43 | 'SCN_GCC_POP' = '' 44 | 'SCN_CLANG_PUSH' = '' 45 | 'SCN_CLANG_IGNORE(...)' = '' 46 | 'SCN_CLANG_POP' = '' 47 | 'SCN_GCC_COMPAT_PUSH' = '' 48 | 'SCN_GCC_COMPAT_IGNORE(...)' = '' 49 | 'SCN_GCC_COMPAT_POP' = '' 50 | 'SCN_MSVC_PUSH' = '' 51 | 'SCN_MSVC_IGNORE(...)' = '' 52 | 'SCN_MSVC_POP' = '' 53 | 'NANO_BEGIN_NAMESPACE' = '' 54 | 'NANO_END_NAMESPACE' = '' 55 | 56 | [code_blocks] 57 | macros = ['SCN_[A-Z0-9_]+?'] 58 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | poxy 2 | -------------------------------------------------------------------------------- /docs/script/monospace-headers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(document).ready(function() { 4 | // Find all `
  • ` elements inside `#poxy-toc` 5 | $('#poxy-toc').find('ul').children('li') 6 | // Additionally, find all headers 7 | .add('h1, h2, h3, h4, h5') 8 | // Replace: 9 | // - <code>...</code> -> ... 10 | // - likewise for (replace with , too) 11 | .each(function () { 12 | $(this).html($(this).html() 13 | .replace(/<code>(.*?)<\/code>/, '$1') 14 | .replace(/<tt>(.*?)<\/tt>/, '$1')); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT SCN_EXAMPLES) 2 | return() 3 | endif() 4 | 5 | add_library(scn_examples_base INTERFACE) 6 | target_link_libraries(scn_examples_base INTERFACE scn_sanitizers) 7 | 8 | add_executable(scn_example_1 example_1.cpp) 9 | target_link_libraries(scn_example_1 scn::scn scn_examples_base) 10 | 11 | add_executable(scn_example_2 example_2.cpp) 12 | target_link_libraries(scn_example_2 scn::scn scn_examples_base) 13 | 14 | add_executable(scn_example_3 example_3.cpp) 15 | target_link_libraries(scn_example_3 scn::scn scn_examples_base) 16 | 17 | add_executable(scn_example_4 example_4.cpp) 18 | target_link_libraries(scn_example_4 scn::scn scn_examples_base) 19 | 20 | add_executable(scn_example_5 example_5.cpp) 21 | target_link_libraries(scn_example_5 scn::scn scn_examples_base) 22 | -------------------------------------------------------------------------------- /examples/example_1.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | if (const auto result = 23 | scn::prompt("What's your favorite number? ", "{}")) { 24 | // `result` is true, we have a successful read. 25 | // Read the single parsed value with result->value() 26 | std::printf("%d, interesting\n", result->value()); 27 | } 28 | else { 29 | std::puts("Well, never mind then."); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/example_2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | if (const auto result = 23 | scn::prompt("Input your full name:\n", "{:[^\n]}")) { 24 | // Access the tuple of all parsed values through result->values() 25 | const auto& [name] = result->values(); 26 | std::printf("Hello, %s", name.c_str()); 27 | } 28 | else { 29 | // Access error object through result.error() 30 | std::printf("Couldn't read name: %d '%s'", result.error().code(), 31 | result.error().msg()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/example_3.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | #include 21 | 22 | int main() 23 | { 24 | std::string source{ 25 | "1 2 3 4 5 6 7 8 9 11 22 33 44 55 66 77 88 99 111 222 333 444 555 666 " 26 | "777 888 999"}; 27 | std::vector integers; 28 | auto input = scn::ranges::subrange{source}; 29 | 30 | while (auto result = scn::scan(input, "{}")) { 31 | integers.push_back(result->value()); 32 | input = result->range(); 33 | } 34 | 35 | for (auto i : integers) { 36 | std::printf("%d\n", i); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/example_4.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | int main() 25 | { 26 | std::string source{R"([{1: 2, 3: 4}, {5: 6}])"}; 27 | 28 | // Use monadic interface of scn::expected (return type of scn::scan) 29 | scn::scan>>(source, "{}") 30 | .transform([](auto result) { 31 | const auto& data = result.value(); 32 | const auto& a = *(data[0].begin()); 33 | const auto& b = *std::next(data[0].begin()); 34 | const auto& c = *(data[1].begin()); 35 | 36 | std::printf(R"([{%d: %d, %d: %d}, {%d: %d}])", a.first, a.second, 37 | b.first, b.second, c.first, c.second); 38 | }) 39 | .transform_error([](scn::scan_error) { std::puts("failure"); }); 40 | } 41 | -------------------------------------------------------------------------------- /examples/example_5.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | std::puts("Write two integers:"); 23 | 24 | if (auto result = scn::scan(stdin, "{}")) { 25 | if (auto second_result = scn::scan(result->file(), "{}")) { 26 | std::printf("Two integers: %d %d\n", result->value(), 27 | second_result->value()); 28 | return 0; 29 | } 30 | 31 | std::string buf{}; 32 | buf.resize(256); 33 | std::ignore = std::fgets(buf.data(), 255, stdin); 34 | 35 | std::printf("First integer: %d, rest of the line: %s", result->value(), 36 | buf.c_str()); 37 | return 0; 38 | } 39 | 40 | std::string buf{}; 41 | buf.resize(256); 42 | std::ignore = std::fgets(buf.data(), 255, stdin); 43 | 44 | std::printf("Entire line: %s", buf.c_str()); 45 | } 46 | -------------------------------------------------------------------------------- /scripts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (SCN_COVERAGE) 2 | add_custom_target(scn_coverage_script_prepare ALL 3 | COMMAND ${CMAKE_COMMAND} -E copy 4 | "${CMAKE_CURRENT_LIST_DIR}/coverage.sh" 5 | "${CMAKE_BINARY_DIR}" 6 | COMMENT "Copying coverage script") 7 | endif() 8 | -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Clean up 4 | rm -rf ./coverage-*.info ./coverage-html 5 | lcov --zerocounters --directory . 6 | 7 | # Build 8 | cmake --build . --target scn_tests --parallel 9 | cmake --build . --target scn_impl_tests --parallel 10 | 11 | # Baseline lcov 12 | lcov --capture --initial --directory . --output-file coverage-base.info 13 | 14 | # Run tests 15 | ctest --output-on-failure 16 | 17 | # Capture and combine lcov data 18 | lcov --capture --directory . --output-file coverage-test.info 19 | lcov --add-tracefile coverage-base.info --add-tracefile coverage-test.info --output-file coverage-total.info 20 | 21 | # Filter lcov data 22 | lcov --remove coverage-total.info \ 23 | '/usr/*' '*/tests/*' '*/examples/*' '*/benchmark/*' '*/src/scn/impl/external/*' '*/include/scn/util/expected_impl.h' '*/_deps/*' \ 24 | --output-file coverage-filtered.info 25 | 26 | # Display summary 27 | lcov --list coverage-filtered.info 28 | 29 | # Generate html 30 | mkdir coverage-html 31 | genhtml --prefix $(dirname $(pwd)) coverage-filtered.info --legend --output-directory=coverage-html 32 | 33 | # See results: 34 | # firefox ./coverage-html/index.html 35 | -------------------------------------------------------------------------------- /scripts/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2017 Elias Kosunen 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | # This file is adapted from a similar script from toml++: 7 | # https://github.com/marzer/tomlplusplus/blob/master/tools/version.py 8 | # Copyright (c) Mark Gillard 9 | # toml++ is licensed under the MIT license 10 | 11 | import sys 12 | import re 13 | from argparse import ArgumentParser 14 | from pathlib import Path 15 | 16 | 17 | def read_text_file(path): 18 | print(rf'Reading {path}') 19 | with open(path, r'r', encoding=r'utf-8') as f: 20 | return f.read() 21 | 22 | 23 | def write_text_file(path, text): 24 | print(rf'Writing {path}') 25 | with open(path, r'w', encoding=r'utf-8', newline='\n') as f: 26 | f.write(text) 27 | 28 | 29 | if __name__ == '__main__': 30 | 31 | args = ArgumentParser(r'version.py', description=r'Sets the project version in all the necessary places.') 32 | args.add_argument(r'version', type=str) 33 | args = args.parse_args() 34 | 35 | version = re.fullmatch(r'\s*[vV]?\s*([0-9]+)\s*[.,;]+\s*([0-9]+)\s*[.,;]+\s*([0-9]+)\s*', args.version) 36 | if not version: 37 | print(rf"Couldn't parse version triplet from '{args.version}'", file=sys.stderr) 38 | sys.exit(1) 39 | version = (int(version[1]), int(version[2]), int(version[3])) 40 | version_str = rf'{version[0]}.{version[1]}.{version[2]}' 41 | print(rf'version: {version_str}') 42 | 43 | root = Path(__file__).parent.parent.resolve() 44 | 45 | path = root / r'CMakeLists.txt' 46 | text = read_text_file(path) 47 | text = re.sub(r'''(\s|^)VERSION\s+[0-9](?:[.][0-9]){2}''', rf"\1VERSION {version_str}", text, count=1, flags=re.I) 48 | write_text_file(path, text) 49 | 50 | path = root / r'include/scn/fwd.h' 51 | text = read_text_file(path) 52 | text = re.sub(r'''(\s*#\s*define\s+SCN_VERSION)\s+SCN_COMPILER\([0-9]+,\s*[0-9]+,\s*[0-9]+\)''', 53 | rf"\1 SCN_COMPILER({version[0]}, {version[1]}, {version[2]})", text) 54 | write_text_file(path, text) 55 | 56 | noop_sub = r'#$%^nbsp^%$#' 57 | path = root / r'docs/pages/mainpage.md' 58 | text = read_text_file(path) 59 | text = re.sub(r'''(GIT_TAG\s+)(?:v\s*)?[0-9](?:[.][0-9]){2}''', rf"\1v{version_str}", text, flags=re.I) 60 | text = text.replace(noop_sub, '') 61 | write_text_file(path, text) 62 | 63 | print('Remember to also:') 64 | print(' - Add an entry to CHANGELOG.md') 65 | print(' - Update definition of SCN_BEGIN_NAMESPACE, if necessary') 66 | print(' - Bump SOVERSION, if necessary') 67 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(unittests) 2 | add_subdirectory(fuzz) 3 | -------------------------------------------------------------------------------- /tests/clang-tidy/.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*, 2 | clang-analyzer-*, 3 | cppcoreguidelines-*, 4 | -cppcoreguidelines-pro-bounds-constant-array-index, 5 | -cppcoreguidelines-avoid-magic-numbers, 6 | modernize-*, 7 | -modernize-use-nodiscard, 8 | -modernize-use-trailing-return-type, 9 | misc-*, 10 | performance-*, 11 | bugprone-*, 12 | -bugprone-easily-swappable-parameters, 13 | hicpp-*, 14 | -hicpp-braces-around-statements, 15 | -hicpp-signed-bitwise, 16 | -hicpp-named-parameter, 17 | cert-*, 18 | -cert-dcl21-cpp, 19 | google-*, 20 | -google-readability-*, 21 | google-readability-namespace-comments, 22 | -google-runtime-int 23 | ' 24 | CheckOptions: 25 | - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic 26 | value: true 27 | - key: misc-const-correctness.WarnPointersAsValues 28 | value: true 29 | -------------------------------------------------------------------------------- /tests/clang-tidy/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | // Include everything 19 | 20 | #include "../../include/scn/all.h" 21 | 22 | #include "../../src/scn/impl/algorithms/contiguous_range_factory.h" 23 | #include "../../src/scn/impl/algorithms/read.h" 24 | #include "../../src/scn/impl/algorithms/take_width_view.h" 25 | 26 | #include "../../src/scn/impl/reader/reader.h" 27 | #include "../../src/scn/impl/unicode/unicode.h" 28 | 29 | #include "../../src/scn/impl/locale.h" 30 | #include "../../src/scn/vscan_impl.h" 31 | 32 | int main() 33 | { 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /tests/consumer-test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(scn-consumer CXX) 4 | 5 | find_package(scn CONFIG REQUIRED) 6 | 7 | add_executable(scn-consumer main.cpp) 8 | target_link_libraries(scn-consumer PRIVATE scn::scn) 9 | -------------------------------------------------------------------------------- /tests/consumer-test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | auto result = scn::input("{}"); 7 | if (!result) { 8 | return 1; 9 | } 10 | std::printf("%d", result->value()); 11 | } 12 | -------------------------------------------------------------------------------- /tests/fuzz/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT SCN_FUZZING) 2 | return() 3 | endif () 4 | 5 | add_library(scn_fuzz_base INTERFACE) 6 | target_link_libraries(scn_fuzz_base INTERFACE scn scn_fuzzer scn_internal) 7 | 8 | function(add_fuzzer name) 9 | add_executable(scn_fuzz_${name} 10 | fuzz.h 11 | ${name}_fuzz.cpp) 12 | target_link_libraries(scn_fuzz_${name} PRIVATE scn scn_internal) 13 | if (SCN_FUZZING_LDFLAGS) 14 | target_link_options(scn_fuzz_${name} PRIVATE ${SCN_FUZZING_LDFLAGS}) 15 | else () 16 | target_link_libraries(scn_fuzz_${name} PRIVATE scn_fuzzer) 17 | endif () 18 | endfunction() 19 | 20 | add_fuzzer(int) 21 | add_fuzzer(float) 22 | add_fuzzer(string) 23 | add_fuzzer(format) 24 | add_fuzzer(chrono) 25 | add_fuzzer(string_impl) 26 | 27 | add_custom_target(scn_fuzz_prepare ALL 28 | COMMAND ${CMAKE_COMMAND} -E copy 29 | "${CMAKE_CURRENT_LIST_DIR}/run-fuzz.sh" 30 | "${CMAKE_BINARY_DIR}/tests/fuzz" 31 | COMMENT "Copying fuzzer scripts") 32 | -------------------------------------------------------------------------------- /tests/fuzz/chrono_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | #include 21 | 22 | namespace scn::fuzz { 23 | template 24 | void do_basic_run_for_source(Source& source, 25 | const format_strings_type& format_strings) 26 | { 27 | do_basic_run_for_type(source, format_strings); 28 | do_basic_run_for_type(source, format_strings); 29 | do_basic_run_for_type(source, format_strings); 30 | } 31 | 32 | namespace { 33 | void run(const uint8_t* data, size_t size) 34 | { 35 | if (size > max_input_bytes || size == 0) { 36 | return; 37 | } 38 | 39 | auto [sv, wsv_reinterpret, wsv_transcode] = make_input_views(data, size); 40 | 41 | const auto& f = 42 | get_format_strings("{:%T}", "{:%R}", "{:%D}", "{:%F}", 43 | "{:%Y-%m-%dT%H:%M:%S%z}", "{:%a}", "{:%b}"); 44 | do_basic_run(sv, f); 45 | 46 | const auto& wf = get_format_strings( 47 | L"{:%T}", L"{:%R}", L"{:%D}", L"{:%F}", L"{:%Y-%m-%dT%H:%M:%S%z}", 48 | L"{:%a}", L"{:%b}"); 49 | do_basic_run(wsv_reinterpret, wf); 50 | if (!wsv_transcode.empty()) { 51 | do_basic_run(wsv_transcode, wf); 52 | } 53 | } 54 | } // namespace 55 | } // namespace scn::fuzz 56 | 57 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 58 | { 59 | scn::fuzz::run(data, size); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/bool.txt: -------------------------------------------------------------------------------- 1 | "true" 2 | "false" 3 | "0" 4 | "1" 5 | "2" 6 | "\x00" 7 | "\x7f" 8 | "\x0a" 9 | "\xc3\xa4" 10 | " " 11 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/char.txt: -------------------------------------------------------------------------------- 1 | "\x00" 2 | "\x7f" 3 | "\x0a" 4 | "\xc3\xa4" 5 | " " 6 | "a" 7 | "Z" 8 | "0" 9 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/chrono.txt: -------------------------------------------------------------------------------- 1 | "1970" 2 | "2024" 3 | "1" 4 | "12" 5 | "29" 6 | "31" 7 | "12" 8 | "24" 9 | "00" 10 | "0" 11 | "99" 12 | ":" 13 | "." 14 | "-" 15 | "+" 16 | "T" 17 | "Z" 18 | "Europe/Helsinki" 19 | "America/New_York" 20 | "GMT" 21 | "UTC" 22 | "EST" 23 | "Monday" 24 | "Tuesday" 25 | "Wednesday" 26 | "Thursday" 27 | "Friday" 28 | "Saturday" 29 | "Sunday" 30 | "January" 31 | "February" 32 | "March" 33 | "April" 34 | "May" 35 | "June" 36 | "July" 37 | "August" 38 | "September" 39 | "October" 40 | "November" 41 | "December" 42 | "am" 43 | "a.m." 44 | "PM" 45 | "P.M." 46 | "%" 47 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/float.txt: -------------------------------------------------------------------------------- 1 | "0" 2 | "1" 3 | "-1" 4 | "2" 5 | "3" 6 | "4" 7 | "5" 8 | "6" 9 | "7" 10 | "8" 11 | "9" 12 | "a" 13 | "b" 14 | "c" 15 | "d" 16 | "e" 17 | "f" 18 | "A" 19 | "B" 20 | "C" 21 | "D" 22 | "E" 23 | "F" 24 | "\x00" 25 | "\x7f" 26 | "\x0a" 27 | "\xc3\xa4" 28 | " " 29 | "." 30 | "," 31 | "p" 32 | "x" 33 | "e" 34 | "-" 35 | "+" 36 | "inf" 37 | "infinity" 38 | "nan" 39 | "NaN" 40 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/format.txt: -------------------------------------------------------------------------------- 1 | # general 2 | "{" 3 | ":" 4 | "}" 5 | "^" 6 | "<" 7 | ">" 8 | "1" 9 | "0" 10 | "-1" 11 | "L" 12 | "{{" 13 | "}}" 14 | "\\x" 15 | "\\u" 16 | "\\U" 17 | " " 18 | # oddballs 19 | "\\" 20 | "\\\\" 21 | "\x00" 22 | "\x7f" 23 | "\x0a" 24 | "\xc3\xa4" 25 | # types 26 | "i" 27 | "b" 28 | "B02" 29 | "B36" 30 | "o" 31 | "x" 32 | "d" 33 | "u" 34 | "a" 35 | "A" 36 | "e" 37 | "E" 38 | "f" 39 | "F" 40 | "g" 41 | "G" 42 | "s" 43 | "c" 44 | "U" 45 | "n" 46 | "'" 47 | # set 48 | "[" 49 | "]" 50 | "-" 51 | "^" 52 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/int.txt: -------------------------------------------------------------------------------- 1 | "0" 2 | "1" 3 | "-1" 4 | "2" 5 | "3" 6 | "4" 7 | "5" 8 | "6" 9 | "7" 10 | "8" 11 | "9" 12 | "a" 13 | "b" 14 | "c" 15 | "d" 16 | "e" 17 | "f" 18 | "A" 19 | "B" 20 | "C" 21 | "D" 22 | "E" 23 | "F" 24 | "-" 25 | "+" 26 | "0x" 27 | "0X" 28 | "0b" 29 | "0B" 30 | "0o" 31 | "0O" 32 | "\x00" 33 | "\x7f" 34 | "\x0a" 35 | "\xc3\xa4" 36 | " " 37 | "," 38 | -------------------------------------------------------------------------------- /tests/fuzz/dictionaries/string.txt: -------------------------------------------------------------------------------- 1 | "a" 2 | "0" 3 | "\x00" 4 | "\x7f" 5 | "\xff" 6 | " " 7 | "\x0a" 8 | "\xc3\xa4" 9 | "\xf0\x9f\x99\x82" 10 | "\xe4\xbd\xa0\xe5\xa5\xbd" 11 | -------------------------------------------------------------------------------- /tests/fuzz/float_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | namespace scn::fuzz { 21 | template 22 | void do_basic_run_for_source(Source& source, 23 | const format_strings_type& format_strings) 24 | { 25 | do_basic_run_for_type(source, format_strings); 26 | do_basic_run_for_type(source, format_strings); 27 | do_basic_run_for_type(source, format_strings); 28 | } 29 | 30 | namespace { 31 | void run(const uint8_t* data, size_t size) 32 | { 33 | if (size > max_input_bytes || size == 0) { 34 | return; 35 | } 36 | 37 | auto [sv, wsv_reinterpret, wsv_transcode] = make_input_views(data, size); 38 | 39 | const auto& f = 40 | get_format_strings("{}", "{:a}", "{:e}", "{:f}", "{:g}", "{:L}"); 41 | do_basic_run(sv, f); 42 | 43 | const auto& wf = get_format_strings(L"{}", L"{:a}", L"{:e}", 44 | L"{:f}", L"{:g}", L"{:L}"); 45 | do_basic_run(wsv_reinterpret, wf); 46 | if (!wsv_transcode.empty()) { 47 | do_basic_run(wsv_transcode, wf); 48 | } 49 | } 50 | } // namespace 51 | } // namespace scn::fuzz 52 | 53 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 54 | { 55 | scn::fuzz::run(data, size); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/fuzz/format_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | namespace scn::fuzz { 21 | namespace { 22 | template 23 | void run_for_type(Source& source) 24 | { 25 | { 26 | auto _ = scn::scan(source, scn::runtime_format(source)); 27 | } 28 | { 29 | auto _ = 30 | scn::scan(global_locale, source, scn::runtime_format(source)); 31 | } 32 | } 33 | 34 | template 35 | void run_for_source(Source& source) 36 | { 37 | using char_type = ranges::range_value_t; 38 | 39 | run_for_type(source); 40 | run_for_type(source); 41 | run_for_type(source); 42 | run_for_type(source); 43 | run_for_type(source); 44 | run_for_type(source); 45 | run_for_type(source); 46 | run_for_type(source); 47 | run_for_type>(source); 48 | } 49 | 50 | void run(const uint8_t* data, size_t size) 51 | { 52 | if (size > max_input_bytes || size == 0) { 53 | return; 54 | } 55 | 56 | auto [sv, wsv_reinterpret, wsv_transcode] = make_input_views(data, size); 57 | 58 | run_for_source(sv); 59 | run_for_source(wsv_reinterpret); 60 | if (!wsv_transcode.empty()) { 61 | run_for_source(wsv_transcode); 62 | } 63 | } 64 | } // namespace 65 | } // namespace scn::fuzz 66 | 67 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 68 | { 69 | scn::fuzz::run(data, size); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /tests/fuzz/int_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | namespace scn::fuzz { 21 | template 22 | void do_basic_run_for_source(Source& source, 23 | const format_strings_type& format_strings) 24 | { 25 | do_basic_run_for_type(source, format_strings); 26 | do_basic_run_for_type(source, format_strings); 27 | do_basic_run_for_type(source, format_strings); 28 | do_basic_run_for_type(source, format_strings); 29 | do_basic_run_for_type(source, format_strings); 30 | do_basic_run_for_type(source, format_strings); 31 | } 32 | 33 | namespace { 34 | void run(const uint8_t* data, size_t size) 35 | { 36 | if (size > max_input_bytes || size == 0) { 37 | return; 38 | } 39 | 40 | auto [sv, wsv_reinterpret, wsv_transcode] = make_input_views(data, size); 41 | 42 | const auto& f = 43 | get_format_strings("{}", "{:i}", "{:d}", "{:L}", "{:x}", "{:b}", 44 | "{:o}", "{:B36}", "{:'}", "{:'L}"); 45 | do_basic_run(sv, f); 46 | 47 | const auto& wf = get_format_strings( 48 | L"{}", L"{:i}", L"{:d}", L"{:L}", L"{:x}", L"{:b}", L"{:o}", L"{:B36}", 49 | L"{:'}", L"{:'L}"); 50 | do_basic_run(wsv_reinterpret, wf); 51 | if (!wsv_transcode.empty()) { 52 | do_basic_run(wsv_transcode, wf); 53 | } 54 | } 55 | } // namespace 56 | } // namespace scn::fuzz 57 | 58 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 59 | { 60 | scn::fuzz::run(data, size); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /tests/fuzz/run-fuzz.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | fuzzer=$1 4 | if [ "$fuzzer" = "string_impl" ]; then 5 | data="string" 6 | else 7 | data=$fuzzer 8 | fi 9 | 10 | repo_dir=$(git rev-parse --show-toplevel) 11 | data_dir=$repo_dir/tests/fuzz 12 | script_dir=$(dirname "$(readlink -f $0)") 13 | 14 | fuzzer_bin=$script_dir/scn_fuzz_$fuzzer 15 | dict_file=$data_dir/dictionaries/$data.txt 16 | corpus_dir=$script_dir/corpora/$fuzzer 17 | seed_dir=$data_dir/seed-corpora/$data 18 | 19 | mkdir -p "$corpus_dir" 20 | echo "Running $fuzzer_bin, with dictionary at $dict_file and seed corpus at $seed_dir, generating corpus at $corpus_dir" 21 | echo Running command: $fuzzer_bin -dict="$dict_file" "$corpus_dir" "$seed_dir" -rss_limit_mb=4096 22 | $fuzzer_bin -dict="$dict_file" "$corpus_dir" "$seed_dir" -rss_limit_mb=4096 23 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/bool/1: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/bool/2: -------------------------------------------------------------------------------- 1 | false 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/bool/3: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/bool/4: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/char/1: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/char/2: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/char/3: -------------------------------------------------------------------------------- 1 | ä 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/char/4: -------------------------------------------------------------------------------- 1 | 😂 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/1: -------------------------------------------------------------------------------- 1 | 2024-08-29T23:41:10+02:00 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/2: -------------------------------------------------------------------------------- 1 | Thursday, Aug 29 2024, 23:41:00, Europe/Helsinki 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/3: -------------------------------------------------------------------------------- 1 | 2024-08-29 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/4: -------------------------------------------------------------------------------- 1 | 23:41 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/5: -------------------------------------------------------------------------------- 1 | 11:41 PM 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/chrono/6: -------------------------------------------------------------------------------- 1 | 08/29/24 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/1: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/10: -------------------------------------------------------------------------------- 1 | 0x1.ffffep+128 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/11: -------------------------------------------------------------------------------- 1 | 0x1p-1074 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/12: -------------------------------------------------------------------------------- 1 | 0x1p-1075 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/13: -------------------------------------------------------------------------------- 1 | 0x1.ffffffffffffffp+1023 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/14: -------------------------------------------------------------------------------- 1 | 0x1.fffffffffffffp+1024 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/15: -------------------------------------------------------------------------------- 1 | 0.0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/16: -------------------------------------------------------------------------------- 1 | 0.0p0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/17: -------------------------------------------------------------------------------- 1 | 1.0e2 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/18: -------------------------------------------------------------------------------- 1 | inf 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/19: -------------------------------------------------------------------------------- 1 | NaN 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/2: -------------------------------------------------------------------------------- 1 | 1.0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/20: -------------------------------------------------------------------------------- 1 | 3.14159265357989 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/3: -------------------------------------------------------------------------------- 1 | 0x1p+2 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/4: -------------------------------------------------------------------------------- 1 | 0x1p-2 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/5: -------------------------------------------------------------------------------- 1 | 0x1.fp+2 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/6: -------------------------------------------------------------------------------- 1 | 0x1.fp-2 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/7: -------------------------------------------------------------------------------- 1 | 0x1p-149 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/8: -------------------------------------------------------------------------------- 1 | 0x1p-150 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/float/9: -------------------------------------------------------------------------------- 1 | 0x1.fffffp+127 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/1: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/10: -------------------------------------------------------------------------------- 1 | abc{}de 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/11: -------------------------------------------------------------------------------- 1 | {1}{0} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/12: -------------------------------------------------------------------------------- 1 | {:x} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/13: -------------------------------------------------------------------------------- 1 | {:*^3L'nx} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/14: -------------------------------------------------------------------------------- 1 | {:<3} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/15: -------------------------------------------------------------------------------- 1 | {:[]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/16: -------------------------------------------------------------------------------- 1 | {:[\x00]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/17: -------------------------------------------------------------------------------- 1 | {:[^foo]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/18: -------------------------------------------------------------------------------- 1 | {:[b-a-r]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/19: -------------------------------------------------------------------------------- 1 | {:[\UDEADBEEF]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/2: -------------------------------------------------------------------------------- 1 | {:} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/20: -------------------------------------------------------------------------------- 1 | {:[:all]} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/3: -------------------------------------------------------------------------------- 1 | {0} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/4: -------------------------------------------------------------------------------- 1 | {foo} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/5: -------------------------------------------------------------------------------- 1 | {:bar} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/6: -------------------------------------------------------------------------------- 1 | {: 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/7: -------------------------------------------------------------------------------- 1 | {}{}{}{}{} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/8: -------------------------------------------------------------------------------- 1 | {} {} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/format/9: -------------------------------------------------------------------------------- 1 | {{}} 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/1: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/10: -------------------------------------------------------------------------------- 1 | 32767 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/11: -------------------------------------------------------------------------------- 1 | 32768 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/12: -------------------------------------------------------------------------------- 1 | -32768 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/13: -------------------------------------------------------------------------------- 1 | -32769 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/14: -------------------------------------------------------------------------------- 1 | 65536 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/15: -------------------------------------------------------------------------------- 1 | 2147483647 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/16: -------------------------------------------------------------------------------- 1 | 2147483648 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/17: -------------------------------------------------------------------------------- 1 | -2147483648 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/18: -------------------------------------------------------------------------------- 1 | -2147483649 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/19: -------------------------------------------------------------------------------- 1 | 4294967295 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/2: -------------------------------------------------------------------------------- 1 | 0123456789 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/20: -------------------------------------------------------------------------------- 1 | 4294967296 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/21: -------------------------------------------------------------------------------- 1 | 9223372036854775807 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/22: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliaskosunen/scnlib/76c9ae63b164a9fc68064d593936b81636d91080/tests/fuzz/seed-corpora/int/22 -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/23: -------------------------------------------------------------------------------- 1 | -9223372036854775808 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/24: -------------------------------------------------------------------------------- 1 | -9223372036854775809 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/25: -------------------------------------------------------------------------------- 1 | 18446744073709709551615 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/26: -------------------------------------------------------------------------------- 1 | 18446744073709709551616 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/27: -------------------------------------------------------------------------------- 1 | 99999999999999999999999 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/28: -------------------------------------------------------------------------------- 1 | -99999999999999999999999 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/29: -------------------------------------------------------------------------------- 1 | 123,456 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/3: -------------------------------------------------------------------------------- 1 | 0xffffffff 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/4: -------------------------------------------------------------------------------- 1 | 127 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/5: -------------------------------------------------------------------------------- 1 | 128 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/6: -------------------------------------------------------------------------------- 1 | -128 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/7: -------------------------------------------------------------------------------- 1 | -129 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/8: -------------------------------------------------------------------------------- 1 | 255 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/int/9: -------------------------------------------------------------------------------- 1 | 256 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/string/1: -------------------------------------------------------------------------------- 1 | Hello world! 2 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/string/2: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet 2 | 3 | yadda yadda yadda 4 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/string/3: -------------------------------------------------------------------------------- 1 | åååäää 2 | ööö 3 | -------------------------------------------------------------------------------- /tests/fuzz/seed-corpora/string/4: -------------------------------------------------------------------------------- 1 | 😂 2 | -------------------------------------------------------------------------------- /tests/fuzz/string_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | namespace scn::fuzz { 21 | template 22 | void do_basic_run_for_source(Source& source, 23 | const format_strings_type& format_strings) 24 | { 25 | do_basic_run_for_type>(source, 26 | format_strings); 27 | if constexpr (scn::ranges::contiguous_range) { 28 | do_basic_run_for_type>( 29 | source, format_strings); 30 | } 31 | } 32 | 33 | namespace { 34 | void run(const uint8_t* data, size_t size) 35 | { 36 | if (size > max_input_bytes || size == 0) { 37 | return; 38 | } 39 | 40 | auto [sv, wsv_reinterpret, wsv_transcode] = make_input_views(data, size); 41 | 42 | const auto& f = get_format_strings("{}", "{:L}", "{:s}", "{:64c}", 43 | "{:64U}", "{:[A-Za-z]}"); 44 | do_basic_run(sv, f); 45 | 46 | const auto& wf = get_format_strings( 47 | L"{}", L"{:L}", L"{:s}", L"{:64c}", L"{:64U}", L"{:[A-Za-z]}"); 48 | do_basic_run(wsv_reinterpret, wf); 49 | if (!wsv_transcode.empty()) { 50 | do_basic_run(wsv_transcode, wf); 51 | } 52 | } 53 | } // namespace 54 | } // namespace scn::fuzz 55 | 56 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 57 | { 58 | scn::fuzz::run(data, size); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /tests/fuzz/string_impl_fuzz.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "fuzz.h" 19 | 20 | #include 21 | 22 | namespace { 23 | template 24 | void do_find(std::string_view sv, Cb cb) 25 | { 26 | auto it = sv.begin(); 27 | while (it != sv.end()) { 28 | SCN_EXPECT(it < sv.end()); 29 | auto in = std::string_view{&*it, static_cast(sv.end() - it)}; 30 | SCN_EXPECT(!in.empty()); 31 | it = cb(in); 32 | SCN_ENSURE(it <= sv.end()); 33 | if (it != sv.end()) 34 | ++it; 35 | SCN_ENSURE(it <= sv.end()); 36 | } 37 | } 38 | } // namespace 39 | 40 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 41 | { 42 | auto sv = std::string_view{reinterpret_cast(data), size}; 43 | do_find(sv, scn::impl::find_classic_space_narrow_fast); 44 | do_find(sv, scn::impl::find_classic_nonspace_narrow_fast); 45 | do_find(sv, scn::impl::find_nondecimal_digit_narrow_fast); 46 | 47 | std::wstring widened{}; 48 | scn::impl::transcode_to_string(sv, widened); 49 | 50 | std::string narrowed{}; 51 | scn::impl::transcode_to_string(std::wstring_view{widened}, narrowed); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /tests/unittests/args_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | using ::testing::Test; 23 | 24 | TEST(ArgsTest, ArgTypeMapping) 25 | { 26 | static_assert(scn::detail::mapped_type_constant::value == 27 | scn::detail::arg_type::int_type); 28 | static_assert(scn::detail::mapped_type_constant::value == 30 | scn::detail::arg_type::custom_type); 31 | 32 | // narrow context, narrow char -> valid 33 | static_assert(scn::detail::mapped_type_constant::value == 34 | scn::detail::arg_type::narrow_character_type); 35 | static_assert(scn::detail::mapped_type_constant::value == 36 | scn::detail::arg_type::narrow_string_type); 37 | static_assert( 38 | scn::detail::mapped_type_constant::value == 39 | scn::detail::arg_type::string_view_type); 40 | 41 | // wide context, wide char -> valid 42 | static_assert(scn::detail::mapped_type_constant::value == 43 | scn::detail::arg_type::wide_character_type); 44 | static_assert( 45 | scn::detail::mapped_type_constant::value == 46 | scn::detail::arg_type::wide_string_type); 47 | static_assert( 48 | scn::detail::mapped_type_constant::value == 49 | scn::detail::arg_type::string_view_type); 50 | 51 | // narrow context, wide char -> valid for chars and strings, invalid for 52 | // string_views 53 | static_assert(scn::detail::mapped_type_constant::value == 54 | scn::detail::arg_type::wide_character_type); 55 | static_assert( 56 | scn::detail::mapped_type_constant::value == 57 | scn::detail::arg_type::wide_string_type); 58 | static_assert( 59 | std::is_same_v< 60 | scn::detail::mapped_type_constant::type, 61 | scn::detail::unscannable_char>); 62 | 63 | // wide context, narrow char -> invalid for chars and string_views, valid 64 | // for strings 65 | static_assert( 66 | std::is_same_v::type, 67 | scn::detail::unscannable_char>); 68 | static_assert( 69 | scn::detail::mapped_type_constant::value == 70 | scn::detail::arg_type::narrow_string_type); 71 | static_assert( 72 | std::is_same_v< 73 | scn::detail::mapped_type_constant::type, 74 | scn::detail::unscannable_char>); 75 | } 76 | 77 | TEST(ArgsTest, ArgStore) 78 | { 79 | std::tuple args_tuple{}; 80 | auto store = scn::make_scan_args(args_tuple); 81 | auto args = scn::basic_scan_args{store}; 82 | 83 | EXPECT_EQ(scn::detail::get_arg_type(args.get(0)), 84 | scn::detail::arg_type::int_type); 85 | EXPECT_EQ(scn::detail::get_arg_type(args.get(1)), 86 | scn::detail::arg_type::double_type); 87 | 88 | *static_cast(scn::detail::get_arg_value(args.get(0)).ref_value) = 42; 89 | 90 | EXPECT_EQ(std::get<0>(args_tuple), 42); 91 | EXPECT_DOUBLE_EQ(std::get<1>(args_tuple), 0.0); 92 | } 93 | -------------------------------------------------------------------------------- /tests/unittests/buffer_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | #include 23 | 24 | using namespace std::string_view_literals; 25 | 26 | namespace { 27 | template 28 | std::string collect(Range r) 29 | { 30 | std::string str; 31 | for (auto it = scn::ranges::begin(r); it != scn::ranges::end(r); ++it) { 32 | str.push_back(*it); 33 | } 34 | return str; 35 | } 36 | } // namespace 37 | 38 | TEST(ScanBufferTest, StringView) 39 | { 40 | auto buf = scn::detail::make_string_scan_buffer("foobar"sv); 41 | static_assert(std::is_same_v>); 43 | 44 | EXPECT_TRUE(buf.is_contiguous()); 45 | EXPECT_EQ(buf.chars_available(), 6); 46 | EXPECT_EQ(collect(buf.get()), "foobar"); 47 | EXPECT_EQ(std::string_view(buf.get_contiguous().data(), 48 | buf.get_contiguous().size()), 49 | "foobar"); 50 | } 51 | 52 | TEST(ScanBufferTest, Deque) 53 | { 54 | auto src = "foobar"sv; 55 | auto deque = std::deque{}; 56 | std::copy(src.begin(), src.end(), std::back_inserter(deque)); 57 | 58 | auto buf = scn::detail::make_forward_scan_buffer(deque); 59 | 60 | auto it = buf.get().begin(); 61 | EXPECT_NE(it, buf.get().end()); 62 | ++it; 63 | EXPECT_EQ(*it, 'o'); 64 | ++it; 65 | EXPECT_NE(it, buf.get().end()); 66 | EXPECT_EQ(*it, 'o'); 67 | ++it; 68 | ++it; 69 | EXPECT_EQ(*it, 'a'); 70 | 71 | auto last_it = it; 72 | it = buf.get().begin(); 73 | ++it; 74 | EXPECT_NE(it, buf.get().end()); 75 | 76 | EXPECT_EQ(collect(scn::ranges::subrange{it, last_it}), "oob"); 77 | EXPECT_EQ(collect(scn::ranges::subrange{it, buf.get().end()}), "oobar"); 78 | } 79 | 80 | TEST(ScanBufferTest, Deque2) 81 | { 82 | auto src = "abc"sv; 83 | auto deque = std::deque{}; 84 | std::copy(src.begin(), src.end(), std::back_inserter(deque)); 85 | 86 | auto buf = scn::detail::make_forward_scan_buffer(deque); 87 | 88 | auto it = buf.get().begin(); 89 | EXPECT_NE(it, buf.get().end()); 90 | EXPECT_EQ(*it, 'a'); 91 | ++it; 92 | EXPECT_NE(it, buf.get().end()); 93 | EXPECT_EQ(*it, 'b'); 94 | 95 | auto cached_it = it; 96 | ++it; 97 | ++it; 98 | EXPECT_EQ(it, buf.get().end()); 99 | EXPECT_EQ(collect(scn::ranges::subrange{cached_it, 100 | std::next(buf.get().begin(), 2)}), 101 | "b"); 102 | EXPECT_EQ(collect(scn::ranges::subrange{cached_it, it}), "bc"); 103 | } 104 | -------------------------------------------------------------------------------- /tests/unittests/char_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | #include 22 | 23 | TEST(CharTest, CharFromNarrow) 24 | { 25 | auto result = scn::scan("abc", "{}"); 26 | ASSERT_TRUE(result); 27 | auto [ch] = result->values(); 28 | EXPECT_EQ(ch, 'a'); 29 | } 30 | TEST(CharTest, CharFromWide) 31 | { 32 | static_assert(!scn::detail::is_scannable::value); 33 | } 34 | 35 | TEST(CharTest, WcharFromNarrow) 36 | { 37 | auto result = scn::scan("abc", "{}"); 38 | ASSERT_TRUE(result); 39 | auto [ch] = result->values(); 40 | EXPECT_EQ(ch, L'a'); 41 | } 42 | TEST(CharTest, WcharFromWide) 43 | { 44 | auto result = scn::scan(L"abc", L"{}"); 45 | EXPECT_TRUE(result); 46 | auto [ch] = result->values(); 47 | EXPECT_EQ(ch, L'a'); 48 | } 49 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(icm_build_failure_testing) 2 | 3 | set(buildfail_sources 4 | brace_as_fill_character.cpp 5 | charset_empty.cpp 6 | charset_reversed_range.cpp 7 | charset_unterminated.cpp 8 | integer_with_string_presentation.cpp 9 | invalid_unicode_in_format_string.cpp 10 | letters_in_argument_id.cpp 11 | locale_flag_with_locale_disabled.cpp 12 | locale_flag_with_string.cpp 13 | negative_argument_id.cpp 14 | regex_disabled.cpp 15 | string_view_non_contiguous_source.cpp 16 | unterminated_argument_id.cpp 17 | unterminated_format_specifier.cpp 18 | usertype_non_contiguous_source.cpp 19 | ) 20 | if (NOT SCN_DISABLE_REGEX) 21 | list(APPEND buildfail_sources 22 | regex_empty.cpp 23 | regex_flag_invalid.cpp 24 | regex_flag_multiple.cpp 25 | regex_matches_non_contiguous_source.cpp 26 | regex_no_presentation.cpp 27 | regex_non_contiguous_source.cpp 28 | regex_unterminated.cpp 29 | regex_wide_strings.cpp 30 | ) 31 | endif () 32 | 33 | if ((SCN_CXX_FRONTEND STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 9.0) OR SCN_CXX_FRONTEND STREQUAL "Clang") 34 | # gcc pre-9 has weird error messages 35 | icm_add_multiple_build_failure_tests( 36 | SOURCES ${buildfail_sources} 37 | PREFIX scn-compilefail 38 | LIBRARIES scn 39 | ) 40 | else () 41 | # We expect the error messages to contain the call to on_error("..."), 42 | # which contains the library error message. 43 | # Seemingly, only gcc and clang do that. 44 | message(STATUS "Compiler frontend not gcc or clang -- build failure tests don't check the compiler output") 45 | foreach (source ${buildfail_sources}) 46 | get_filename_component(source_name ${source} NAME_WE) 47 | icm_add_build_failure_test( 48 | NAME scn-compilefail-${source_name} 49 | LIBRARIES scn 50 | SOURCES ${source} 51 | ) 52 | endforeach () 53 | endif () 54 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/brace_as_fill_character.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid fill character '{' in format string 23 | auto result = scn::scan("42", SCN_STRING("{:{^}")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/charset_empty.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid [character set] specifier in format string 23 | auto result = scn::scan("42", SCN_STRING("{:[}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/charset_reversed_range.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid range in [character set] format string 23 | auto result = scn::scan("42", SCN_STRING("{:[9-0]}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/charset_unterminated.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid [character set] specifier in format string 23 | auto result = scn::scan("42", SCN_STRING("{:[}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/integer_with_string_presentation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid type specifier for integer type 23 | auto result = scn::scan("42", SCN_STRING("{:s}")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/invalid_unicode_in_format_string.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid encoding in format string 23 | auto result = scn::scan("42", SCN_STRING("\xa4")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/letters_in_argument_id.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid argument ID 23 | auto result = scn::scan("42", SCN_STRING("{1a}")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/locale_flag_with_locale_disabled.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #define SCN_DISABLE_LOCALE 1 19 | #include 20 | 21 | int main() 22 | { 23 | // build error: 'L' flag invalid when SCN_DISABLE_LOCALE is on 24 | auto result = scn::scan("42", SCN_STRING("{:L}")); 25 | return result && result->value() == 42; 26 | } 27 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/locale_flag_with_string.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: 'L' specifier can only be used 23 | auto result = scn::scan("42", SCN_STRING("{:L}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/negative_argument_id.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid argument ID 23 | auto result = scn::scan("42", SCN_STRING("{-1}")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_disabled.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #define SCN_DISABLE_REGEX 1 19 | #include 20 | 21 | int main() 22 | { 23 | // build error: Regular expression support is disabled 24 | auto result = scn::scan("42", SCN_STRING("{:/[a-z]+/}")); 25 | return result && result->value() == "42"; 26 | } 27 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_empty.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid (empty) regex in format string 23 | auto result = scn::scan("42", SCN_STRING("{://}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_flag_invalid.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid flag in regex 23 | auto result = scn::scan("42", SCN_STRING("{:/[a-z]+/a}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_flag_multiple.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Flag set multiple times in regex 23 | auto result = scn::scan("42", SCN_STRING("{:/[a-z]+/ii}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_matches_non_contiguous_source.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Cannot read a regex from a non-contiguous 23 | auto result = scn::scan(stdin, SCN_STRING("{:/[a-z]+/}")); 24 | return !result.has_value(); 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_no_presentation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | #include 20 | 21 | int main() 22 | { 23 | // build error: Regular expression needs to be specified 24 | auto result = scn::scan("42", SCN_STRING("{}")); 25 | return !result.has_value(); 26 | } 27 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_non_contiguous_source.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Cannot read a regex from a non-contiguous 23 | auto result = scn::scan(stdin, SCN_STRING("{:/[a-z]+/}")); 24 | return !result.has_value(); 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_unterminated.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Unexpected end of regex in format string 23 | auto result = scn::scan("42", SCN_STRING("{:/[a-z]+}")); 24 | return result && result->value() == "42"; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/regex_wide_strings.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | int main() 23 | { 24 | #if !SCN_REGEX_SUPPORTS_WIDE_STRINGS 25 | // build error: Regex backend doesn't support wide strings as input 26 | auto result = scn::scan(L"42", SCN_STRING(L"{:/[a-z]+/}")); 27 | return result && result->value() == L"42"; 28 | #else 29 | static_assert(false, "Regex backend doesn't support wide strings as input"); 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/string_view_non_contiguous_source.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Cannot read a string_view from a non-contiguous 23 | auto result = scn::scan(stdin, SCN_STRING("{}")); 24 | return !result.has_value(); 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/unterminated_argument_id.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | // build error: Invalid argument ID 23 | auto result = scn::scan("42", SCN_STRING("{0")); 24 | return result && result->value() == 42; 25 | } 26 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/unterminated_format_specifier.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | int main() 21 | { 22 | { 23 | auto result = scn::scan("42", SCN_STRING("{:d}")); 24 | SCN_UNUSED(result); 25 | } 26 | 27 | { 28 | // build error: Unexpected end of replacement field 29 | auto result = scn::scan("42", SCN_STRING("{:")); 30 | return result && result->value() == 42; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/unittests/compilefail_tests/usertype_non_contiguous_source.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | struct mytype { 21 | int value; 22 | }; 23 | 24 | template <> 25 | struct scn::scanner : public scn::scanner { 26 | template 27 | constexpr auto parse(ParseCtx& pctx) -> typename ParseCtx::iterator 28 | { 29 | if (!pctx.is_source_contiguous() || !pctx.is_source_borrowed()) { 30 | pctx.on_error("Invalid source for mytype"); 31 | } 32 | return scn::scanner::parse(pctx); 33 | } 34 | 35 | template 36 | auto scan(mytype& val, Ctx& ctx) const 37 | -> scn::scan_expected 38 | { 39 | return scn::scanner::scan(val.value, ctx); 40 | } 41 | }; 42 | 43 | int main() 44 | { 45 | // build error: Invalid source for mytype 46 | auto result = scn::scan(stdin, SCN_STRING("{}")); 47 | return !result.has_value(); 48 | } 49 | -------------------------------------------------------------------------------- /tests/unittests/context_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | -------------------------------------------------------------------------------- /tests/unittests/error_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | TEST(ErrorTest, General) 23 | { 24 | const auto eof_error = 25 | scn::scan_error{scn::scan_error::end_of_input, "EOF"}; 26 | const auto invalid_scanned_value_error = 27 | scn::scan_error{scn::scan_error::invalid_scanned_value, ""}; 28 | 29 | EXPECT_EQ(eof_error.code(), scn::scan_error::end_of_input); 30 | EXPECT_EQ(invalid_scanned_value_error.code(), 31 | scn::scan_error::invalid_scanned_value); 32 | 33 | EXPECT_EQ(eof_error, scn::scan_error::end_of_input); 34 | EXPECT_EQ(invalid_scanned_value_error, 35 | scn::scan_error::invalid_scanned_value); 36 | } 37 | 38 | TEST(ErrorTest, ExpectedVoid) 39 | { 40 | const auto good = scn::scan_expected{}; 41 | const auto eof_error = scn::scan_expected{ 42 | scn::unexpected(scn::scan_error{scn::scan_error::end_of_input, "EOF"})}; 43 | const auto invalid_scanned_value_error = 44 | scn::scan_expected{scn::detail::unexpected_scan_error( 45 | scn::scan_error::invalid_scanned_value, "")}; 46 | 47 | EXPECT_TRUE(good); 48 | EXPECT_FALSE(eof_error); 49 | EXPECT_FALSE(invalid_scanned_value_error); 50 | 51 | EXPECT_EQ(eof_error.error().code(), scn::scan_error::end_of_input); 52 | EXPECT_EQ(invalid_scanned_value_error.error().code(), 53 | scn::scan_error::invalid_scanned_value); 54 | 55 | EXPECT_EQ(eof_error.error(), scn::scan_error::end_of_input); 56 | EXPECT_EQ(invalid_scanned_value_error.error(), 57 | scn::scan_error::invalid_scanned_value); 58 | } 59 | -------------------------------------------------------------------------------- /tests/unittests/examples_test_runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import fnmatch 4 | import os 5 | import sys 6 | import subprocess 7 | 8 | 9 | def find_file(pattern, path): 10 | for root, dirs, files in os.walk(path): 11 | for name in files: 12 | if fnmatch.fnmatch(name, pattern): 13 | return os.path.join(root, name) 14 | raise RuntimeError(f"Couldn't find pattern '{pattern}' in {path}") 15 | 16 | 17 | if os.name == 'nt': 18 | examples_file_extension = '.exe' 19 | else: 20 | examples_file_extension = '' 21 | 22 | cmd_args = sys.argv[1:] 23 | if len(cmd_args) != 1: 24 | raise RuntimeError( 25 | f"Expected a single command-line argument, containing path to example binaries, got {cmd_args} instead") 26 | examples_root_dir = os.path.normpath(cmd_args[0]) 27 | examples_bin = find_file(f"scn_example*{examples_file_extension}", examples_root_dir) 28 | examples_dir = os.path.dirname(examples_bin) 29 | 30 | 31 | def check(i, input, expected_output): 32 | path = os.path.join(examples_dir, f"scn_example_{i}{examples_file_extension}") 33 | print(f"Invoking {path}") 34 | result = subprocess.run([path], 35 | shell=True, 36 | input=input, 37 | capture_output=True, 38 | text=True) 39 | if result.returncode != 0: 40 | print(f"scn_example_{i} stdout:\n{result.stdout}\nstderr:\n{result.stderr}") 41 | result.check_returncode() 42 | if result.stdout != expected_output: 43 | raise RuntimeError( 44 | f"scn_example_{i} failed with incorrect output:\n{result.stdout}\nExpected:\n{expected_output}") 45 | print(f"scn_example_{i} successful") 46 | 47 | 48 | check(1, "42", "What's your favorite number? 42, interesting\n") 49 | check(1, "foo", "What's your favorite number? Well, never mind then.\n") 50 | 51 | check(2, "John Doe", "Input your full name:\nHello, John Doe") 52 | 53 | check(3, "", """1 54 | 2 55 | 3 56 | 4 57 | 5 58 | 6 59 | 7 60 | 8 61 | 9 62 | 11 63 | 22 64 | 33 65 | 44 66 | 55 67 | 66 68 | 77 69 | 88 70 | 99 71 | 111 72 | 222 73 | 333 74 | 444 75 | 555 76 | 666 77 | 777 78 | 888 79 | 999 80 | """) 81 | 82 | check(4, "", "[{1: 2, 3: 4}, {5: 6}]") 83 | 84 | check(5, "123 456", "Write two integers:\nTwo integers: 123 456\n") 85 | check(5, "123 abc", "Write two integers:\nFirst integer: 123, rest of the line: abc") 86 | check(5, "abc def", "Write two integers:\nEntire line: abc def") 87 | -------------------------------------------------------------------------------- /tests/unittests/float_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | TEST(FloatTest, FloatWithSuffix) 23 | { 24 | auto result = scn::scan("scn::scan for string_view: 0.0075ms", 25 | "scn::scan for string_view: {}ms"); 26 | ASSERT_TRUE(result); 27 | EXPECT_DOUBLE_EQ(result->value(), 0.0075); 28 | EXPECT_TRUE(result->range().empty()); 29 | } 30 | 31 | TEST(FloatTest, FloatWithDoubleSign) 32 | { 33 | auto result = scn::scan("--4", "{}"); 34 | ASSERT_FALSE(result); 35 | } 36 | 37 | #if SCN_HAS_STD_F16 38 | TEST(FloatTest, Float16) 39 | { 40 | auto result = scn::scan("3.14", "{}"); 41 | if (!result && 42 | result.error().code() == scn::scan_error::type_not_supported) { 43 | GTEST_SKIP(); 44 | } 45 | ASSERT_TRUE(result); 46 | } 47 | #endif 48 | #if SCN_HAS_STD_F32 49 | TEST(FloatTest, Float32) 50 | { 51 | auto result = scn::scan("3.14", "{}"); 52 | if (!result && 53 | result.error().code() == scn::scan_error::type_not_supported) { 54 | GTEST_SKIP(); 55 | } 56 | ASSERT_TRUE(result); 57 | } 58 | #endif 59 | #if SCN_HAS_STD_F64 60 | TEST(FloatTest, Float64) 61 | { 62 | auto result = scn::scan("3.14", "{}"); 63 | if (!result && 64 | result.error().code() == scn::scan_error::type_not_supported) { 65 | GTEST_SKIP(); 66 | } 67 | ASSERT_TRUE(result); 68 | } 69 | #endif 70 | #if SCN_HAS_STD_F128 71 | TEST(FloatTest, Float128) 72 | { 73 | auto result = scn::scan("3.14", "{}"); 74 | if (!result && 75 | result.error().code() == scn::scan_error::type_not_supported) { 76 | GTEST_SKIP(); 77 | } 78 | ASSERT_TRUE(result); 79 | } 80 | #endif 81 | #if SCN_HAS_STD_BF16 82 | TEST(FloatTest, BFloat16) 83 | { 84 | auto result = scn::scan("3.14", "{}"); 85 | if (!result && 86 | result.error().code() == scn::scan_error::type_not_supported) { 87 | GTEST_SKIP(); 88 | } 89 | ASSERT_TRUE(result); 90 | } 91 | #endif 92 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/bits_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "../wrapped_gtest.h" 19 | 20 | #include 21 | 22 | TEST(BitsTest, CountTrailingZeroes) 23 | { 24 | EXPECT_EQ(scn::impl::count_trailing_zeroes(0b0001), 0); 25 | EXPECT_EQ(scn::impl::count_trailing_zeroes(0b1000), 3); 26 | EXPECT_EQ(scn::impl::count_trailing_zeroes(0b1111), 0); 27 | 28 | EXPECT_EQ( 29 | scn::impl::count_trailing_zeroes(std::numeric_limits::max()), 30 | 0); 31 | EXPECT_EQ(scn::impl::count_trailing_zeroes( 32 | std::numeric_limits::max() - 1), 33 | 1); 34 | EXPECT_EQ(scn::impl::count_trailing_zeroes( 35 | std::numeric_limits::max() - 2), 36 | 0); 37 | EXPECT_EQ(scn::impl::count_trailing_zeroes( 38 | std::numeric_limits::max() - 3), 39 | 2); 40 | 41 | EXPECT_EQ(scn::impl::count_trailing_zeroes( 42 | static_cast(std::numeric_limits::max())), 43 | 0); 44 | EXPECT_EQ( 45 | scn::impl::count_trailing_zeroes( 46 | static_cast(std::numeric_limits::max()) - 1), 47 | 1); 48 | EXPECT_EQ( 49 | scn::impl::count_trailing_zeroes( 50 | static_cast(std::numeric_limits::max()) - 2), 51 | 0); 52 | EXPECT_EQ( 53 | scn::impl::count_trailing_zeroes( 54 | static_cast(std::numeric_limits::max()) - 3), 55 | 2); 56 | EXPECT_EQ(scn::impl::count_trailing_zeroes( 57 | static_cast(std::numeric_limits::max())) * 58 | 2, 59 | 0); 60 | } 61 | 62 | TEST(BitsTest, HasZeroByte) 63 | { 64 | EXPECT_TRUE(scn::impl::has_zero_byte(0)); 65 | EXPECT_TRUE(scn::impl::has_zero_byte(0xff)); 66 | EXPECT_FALSE( 67 | scn::impl::has_zero_byte(std::numeric_limits::max())); 68 | EXPECT_TRUE( 69 | scn::impl::has_zero_byte(std::numeric_limits::max() - 0xff)); 70 | } 71 | 72 | TEST(BitsTest, Log2) 73 | { 74 | EXPECT_EQ(scn::impl::log2_fast(1), 0); 75 | EXPECT_EQ(scn::impl::log2_fast(2), 1); 76 | EXPECT_EQ(scn::impl::log2_fast(3), 1); 77 | EXPECT_EQ(scn::impl::log2_fast(4), 2); 78 | EXPECT_EQ(scn::impl::log2_fast(7), 2); 79 | EXPECT_EQ(scn::impl::log2_fast(8), 3); 80 | 81 | EXPECT_EQ(scn::impl::log2_pow2_fast(2), 1); 82 | EXPECT_EQ(scn::impl::log2_pow2_fast(4), 2); 83 | EXPECT_EQ(scn::impl::log2_pow2_fast(8), 3); 84 | } 85 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/bool_reader_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "reader_test_common.h" 19 | 20 | #include 21 | 22 | using namespace std::string_view_literals; 23 | 24 | struct classic_tag {}; 25 | struct localized_tag {}; 26 | 27 | template 28 | class BoolReaderTest : public testing::Test { 29 | protected: 30 | static constexpr bool is_localized = std::is_same_v; 31 | 32 | static auto read_default(std::string_view src, bool& val) 33 | { 34 | #if !SCN_DISABLE_LOCALE 35 | if constexpr (is_localized) { 36 | return scn::impl::bool_reader{}.read_localized(src, {}, val); 37 | } 38 | else 39 | #endif 40 | { 41 | return scn::impl::bool_reader{}.read_classic(src, val); 42 | } 43 | } 44 | }; 45 | 46 | using type_list = testing::Types; 47 | 48 | SCN_CLANG_PUSH 49 | SCN_CLANG_IGNORE("-Wgnu-zero-variadic-macro-arguments") 50 | 51 | TYPED_TEST_SUITE(BoolReaderTest, type_list); 52 | 53 | SCN_CLANG_POP 54 | 55 | TYPED_TEST(BoolReaderTest, DefaultTextualTrue) 56 | { 57 | auto src = "true abc"sv; 58 | bool val{}; 59 | auto ret = this->read_default(src, val); 60 | 61 | ASSERT_TRUE(ret); 62 | EXPECT_EQ(*ret, src.begin() + 4); 63 | EXPECT_TRUE(val); 64 | } 65 | TYPED_TEST(BoolReaderTest, DefaultTextualFalse) 66 | { 67 | auto src = "false abc"sv; 68 | bool val{}; 69 | auto ret = this->read_default(src, val); 70 | 71 | ASSERT_TRUE(ret); 72 | EXPECT_EQ(*ret, src.begin() + 5); 73 | EXPECT_FALSE(val); 74 | } 75 | TYPED_TEST(BoolReaderTest, DefaultTextualNonsense) 76 | { 77 | auto src = "foobar abc"sv; 78 | bool val{}; 79 | auto ret = this->read_default(src, val); 80 | 81 | ASSERT_FALSE(ret); 82 | } 83 | 84 | TYPED_TEST(BoolReaderTest, DefaultNumericTrue) 85 | { 86 | auto src = "1 abc"sv; 87 | bool val{}; 88 | auto ret = this->read_default(src, val); 89 | 90 | ASSERT_TRUE(ret); 91 | EXPECT_EQ(*ret, src.begin() + 1); 92 | EXPECT_TRUE(val); 93 | } 94 | TYPED_TEST(BoolReaderTest, DefaultNumericFalse) 95 | { 96 | auto src = "0 abc"sv; 97 | bool val{}; 98 | auto ret = this->read_default(src, val); 99 | 100 | ASSERT_TRUE(ret); 101 | EXPECT_EQ(*ret, src.begin() + 1); 102 | EXPECT_FALSE(val); 103 | } 104 | TYPED_TEST(BoolReaderTest, DefaultNumericFalsePrefix) 105 | { 106 | auto src = "01abc"sv; 107 | bool val{}; 108 | auto ret = this->read_default(src, val); 109 | 110 | ASSERT_TRUE(ret); 111 | EXPECT_EQ(*ret, src.begin() + 1); 112 | EXPECT_FALSE(val); 113 | } 114 | TYPED_TEST(BoolReaderTest, DefaultNumericNonsense) 115 | { 116 | auto src = "2 abc"sv; 117 | bool val{}; 118 | auto ret = this->read_default(src, val); 119 | 120 | ASSERT_FALSE(ret); 121 | } 122 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/function_ref_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "../wrapped_gtest.h" 19 | 20 | #include 21 | 22 | int identity_fn(int a) 23 | { 24 | return a; 25 | } 26 | 27 | TEST(FunctionRefTest, Test) 28 | { 29 | EXPECT_EQ(scn::impl::function_ref(identity_fn)(42), 42); 30 | 31 | EXPECT_EQ(scn::impl::function_ref([](int a) { return a; })(42), 32 | 42); 33 | 34 | int n = 42; 35 | EXPECT_EQ(scn::impl::function_ref([&]() { return n; })(), 42); 36 | } 37 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/integer_reader_test.impl_narrow_classic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "integer_reader_test.h" 19 | 20 | using TypeList = 21 | ::testing::Types, 22 | int_reader_wrapper, 23 | int_reader_wrapper, 24 | int_reader_wrapper, 25 | int_reader_wrapper, 26 | int_reader_wrapper, 27 | int_reader_wrapper, 28 | int_reader_wrapper, 29 | int_reader_wrapper, 30 | int_reader_wrapper>; 31 | 32 | SCN_CLANG_PUSH 33 | SCN_CLANG_IGNORE("-Wgnu-zero-variadic-macro-arguments") 34 | 35 | INSTANTIATE_TYPED_TEST_SUITE_P(NarrowClassic, IntValueReaderTest, TypeList); 36 | 37 | SCN_CLANG_POP 38 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/integer_reader_test.impl_narrow_localized.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "integer_reader_test.h" 19 | 20 | #if !SCN_DISABLE_LOCALE 21 | 22 | using TypeList = 23 | ::testing::Types, 24 | int_reader_wrapper, 25 | int_reader_wrapper, 26 | int_reader_wrapper, 27 | int_reader_wrapper, 28 | int_reader_wrapper, 29 | int_reader_wrapper, 30 | int_reader_wrapper, 31 | int_reader_wrapper, 32 | int_reader_wrapper>; 33 | 34 | SCN_CLANG_PUSH 35 | SCN_CLANG_IGNORE("-Wgnu-zero-variadic-macro-arguments") 36 | 37 | INSTANTIATE_TYPED_TEST_SUITE_P(NarrowLocalized, IntValueReaderTest, TypeList); 38 | 39 | SCN_CLANG_POP 40 | 41 | #endif // !SCN_DISABLE_LOCALE 42 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/integer_reader_test.impl_wide_classic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "integer_reader_test.h" 19 | 20 | using TypeList = 21 | ::testing::Types, 22 | int_reader_wrapper, 23 | int_reader_wrapper, 24 | int_reader_wrapper, 25 | int_reader_wrapper, 26 | int_reader_wrapper, 27 | int_reader_wrapper, 28 | int_reader_wrapper, 29 | int_reader_wrapper, 30 | int_reader_wrapper>; 31 | 32 | SCN_CLANG_PUSH 33 | SCN_CLANG_IGNORE("-Wgnu-zero-variadic-macro-arguments") 34 | 35 | INSTANTIATE_TYPED_TEST_SUITE_P(WideClassic, IntValueReaderTest, TypeList); 36 | 37 | SCN_CLANG_POP 38 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/integer_reader_test.impl_wide_localized.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "integer_reader_test.h" 19 | 20 | #if !SCN_DISABLE_LOCALE 21 | 22 | using TypeList = 23 | ::testing::Types, 24 | int_reader_wrapper, 25 | int_reader_wrapper, 26 | int_reader_wrapper, 27 | int_reader_wrapper, 28 | int_reader_wrapper, 29 | int_reader_wrapper, 30 | int_reader_wrapper, 31 | int_reader_wrapper, 32 | int_reader_wrapper>; 33 | 34 | SCN_CLANG_PUSH 35 | SCN_CLANG_IGNORE("-Wgnu-zero-variadic-macro-arguments") 36 | 37 | INSTANTIATE_TYPED_TEST_SUITE_P(WideLocalized, IntValueReaderTest, TypeList); 38 | 39 | SCN_CLANG_POP 40 | 41 | #endif // !SCN_DISABLE_LOCALE 42 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/reader_test_common.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #pragma once 19 | 20 | #include "../test_common.h" 21 | 22 | #include 23 | 24 | template 28 | class Reader> 29 | class reader_wrapper { 30 | public: 31 | using char_type = CharT; 32 | using value_type = ValueT; 33 | using reader_type = Reader; 34 | 35 | static constexpr bool is_localized = Localized; 36 | 37 | constexpr reader_wrapper() = default; 38 | 39 | auto read_default(std::basic_string_view source, ValueT& value) 40 | { 41 | return m_reader.read_default(source, value, {}); 42 | } 43 | 44 | auto read_specs(std::basic_string_view source, 45 | const scn::detail::format_specs& specs, 46 | ValueT& value) 47 | { 48 | auto specs_copy = specs; 49 | specs_copy.localized = is_localized; 50 | 51 | return m_reader.read_specs(source, specs_copy, value, {}); 52 | } 53 | 54 | auto read_specs_with_locale(std::basic_string_view source, 55 | const scn::detail::format_specs& specs, 56 | ValueT& value, 57 | scn::detail::locale_ref loc) 58 | { 59 | auto specs_copy = specs; 60 | specs_copy.localized = is_localized; 61 | 62 | return m_reader.read_specs(source, specs_copy, value, loc); 63 | } 64 | 65 | private: 66 | reader_type m_reader{}; 67 | }; 68 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/transcode_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "../wrapped_gtest.h" 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | namespace { 26 | std::string get_file_contents(const std::string& sourcefile) 27 | { 28 | std::ifstream fstr{sourcefile}; 29 | std::stringstream ss; 30 | ss << fstr.rdbuf(); 31 | return ss.str(); 32 | } 33 | } // namespace 34 | 35 | using namespace std::string_view_literals; 36 | 37 | TEST(TranscodeTest, HelloWorld) 38 | { 39 | auto in = "Hello world"sv; 40 | 41 | std::wstring widened{}; 42 | scn::impl::transcode_to_string(in, widened); 43 | EXPECT_EQ(widened, L"Hello world"); 44 | 45 | std::string narrowed{}; 46 | scn::impl::transcode_to_string(std::wstring_view{widened}, narrowed); 47 | EXPECT_EQ(narrowed, "Hello world"); 48 | 49 | widened.clear(); 50 | scn::impl::transcode_valid_to_string(in, widened); 51 | EXPECT_EQ(widened, L"Hello world"); 52 | 53 | narrowed.clear(); 54 | scn::impl::transcode_valid_to_string(std::wstring_view{widened}, narrowed); 55 | EXPECT_EQ(narrowed, "Hello world"); 56 | } 57 | 58 | TEST(TranscodeTest, Lipsum) 59 | { 60 | auto in = get_file_contents("lipsum.txt"); 61 | 62 | std::wstring widened{}; 63 | scn::impl::transcode_to_string(std::string_view{in}, widened); 64 | 65 | std::string narrowed{}; 66 | scn::impl::transcode_to_string(std::wstring_view{widened}, narrowed); 67 | 68 | EXPECT_EQ(narrowed, in); 69 | 70 | widened.clear(); 71 | scn::impl::transcode_valid_to_string(std::string_view{in}, widened); 72 | 73 | narrowed.clear(); 74 | scn::impl::transcode_valid_to_string(std::wstring_view{widened}, narrowed); 75 | 76 | EXPECT_EQ(narrowed, in); 77 | } 78 | 79 | TEST(TranscodeTest, Unicode) 80 | { 81 | auto in = get_file_contents("unicode.txt"); 82 | 83 | std::wstring widened{}; 84 | scn::impl::transcode_to_string(std::string_view{in}, widened); 85 | 86 | std::string narrowed{}; 87 | scn::impl::transcode_to_string(std::wstring_view{widened}, narrowed); 88 | 89 | EXPECT_EQ(narrowed, in); 90 | 91 | widened.clear(); 92 | scn::impl::transcode_valid_to_string(std::string_view{in}, widened); 93 | 94 | narrowed.clear(); 95 | scn::impl::transcode_valid_to_string(std::wstring_view{widened}, narrowed); 96 | 97 | EXPECT_EQ(narrowed, in); 98 | } 99 | -------------------------------------------------------------------------------- /tests/unittests/impl_tests/whitespace_skip_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "../wrapped_gtest.h" 19 | 20 | #include 21 | 22 | namespace { 23 | std::ptrdiff_t PositionOfFirstNonSpace(std::string_view src) 24 | { 25 | auto it = scn::impl::skip_classic_whitespace(src, true); 26 | return scn::detail::to_address(*it) - src.data(); 27 | } 28 | } // namespace 29 | 30 | TEST(WhitespaceSkipTest, AllSpace) 31 | { 32 | EXPECT_EQ(PositionOfFirstNonSpace(" "), 4); 33 | EXPECT_EQ(PositionOfFirstNonSpace(" \n\t "), 4); 34 | 35 | EXPECT_EQ(PositionOfFirstNonSpace(" "), 8); 36 | EXPECT_EQ(PositionOfFirstNonSpace(" \n\t\r\v "), 8); 37 | 38 | EXPECT_EQ(PositionOfFirstNonSpace(" "), 12); 39 | EXPECT_EQ(PositionOfFirstNonSpace(" \n\t\r\v "), 12); 40 | } 41 | 42 | TEST(WhitespaceSkipTest, NoSpace) 43 | { 44 | EXPECT_EQ(PositionOfFirstNonSpace("123 "), 0); 45 | EXPECT_EQ(PositionOfFirstNonSpace("123 "), 0); 46 | EXPECT_EQ(PositionOfFirstNonSpace("123 "), 0); 47 | } 48 | 49 | TEST(WhitespaceSkipTest, NonSpaceAtEnd) 50 | { 51 | EXPECT_EQ(PositionOfFirstNonSpace(" a"), 4); 52 | EXPECT_EQ(PositionOfFirstNonSpace(" \n a"), 4); 53 | 54 | EXPECT_EQ(PositionOfFirstNonSpace(" a"), 8); 55 | EXPECT_EQ(PositionOfFirstNonSpace(" \n a"), 8); 56 | 57 | EXPECT_EQ(PositionOfFirstNonSpace(" a"), 12); 58 | EXPECT_EQ(PositionOfFirstNonSpace(" \n a"), 12); 59 | } 60 | 61 | TEST(WhitespaceSkipTest, SpecialValues) 62 | { 63 | EXPECT_EQ(PositionOfFirstNonSpace("\x80\x80\x80\x80"), 0); 64 | EXPECT_EQ(PositionOfFirstNonSpace("\x80\x80\x80\x80\x80\x80\x80\x80"), 0); 65 | EXPECT_EQ(PositionOfFirstNonSpace( 66 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"), 67 | 0); 68 | 69 | EXPECT_EQ(PositionOfFirstNonSpace("\xff\xff\xff\xff"), 0); 70 | EXPECT_EQ(PositionOfFirstNonSpace("\xff\xff\xff\xff\xff\xff\xff\xff"), 0); 71 | EXPECT_EQ(PositionOfFirstNonSpace( 72 | "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), 73 | 0); 74 | } 75 | -------------------------------------------------------------------------------- /tests/unittests/input_map_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | #include 23 | 24 | #if SCN_HAS_STD_SPAN 25 | #include 26 | #endif 27 | 28 | using ::testing::Test; 29 | 30 | using namespace std::string_view_literals; 31 | 32 | namespace { 33 | template 34 | std::string collect(Range r) 35 | { 36 | std::string str; 37 | for (auto it = scn::ranges::begin(r); it != scn::ranges::end(r); ++it) { 38 | str.push_back(*it); 39 | } 40 | return str; 41 | } 42 | } // namespace 43 | 44 | TEST(InputMapTest, RefBuffer) 45 | { 46 | auto first = scn::detail::make_string_scan_buffer("foobar"sv); 47 | auto second = scn::detail::make_scan_buffer(first.get()); 48 | static_assert(std::is_same_v>); 50 | EXPECT_EQ(collect(second.get()), "foobar"); 51 | } 52 | 53 | #if 0 54 | TEST(InputMapTest, StringView) 55 | { 56 | auto buf = scn::detail::make_scan_buffer("foobar"sv); 57 | static_assert(std::is_same_v>); 59 | EXPECT_EQ(collect(buf.get()), "foobar"); 60 | } 61 | 62 | TEST(InputMapTest, StringLiteral) 63 | { 64 | auto buf = scn::detail::make_scan_buffer("foobar"); 65 | static_assert(std::is_same_v>); 67 | EXPECT_EQ(collect(buf.get()), "foobar"); 68 | } 69 | 70 | TEST(InputMapTest, StdString) 71 | { 72 | auto str = std::string{"foobar"}; 73 | auto buf = scn::detail::make_scan_buffer(str); 74 | static_assert(std::is_same_v>); 76 | EXPECT_EQ(collect(buf.get()), "foobar"); 77 | } 78 | 79 | #if SCN_HAS_STD_SPAN 80 | TEST(InputMapTest, Span) 81 | { 82 | auto str = "foobar"sv; 83 | auto buf = scn::detail::make_scan_buffer( 84 | std::span{str.data(), str.size()}); 85 | static_assert(std::is_same_v>); 87 | EXPECT_EQ(collect(buf.get()), "foobar"); 88 | } 89 | #endif 90 | 91 | TEST(InputMapTest, StringViewTake) 92 | { 93 | auto buf = 94 | scn::detail::make_scan_buffer(scn::ranges::take_view("foobar"sv, 3)); 95 | static_assert(std::is_same_v>); 97 | EXPECT_EQ(collect(buf.get()), "foo"); 98 | } 99 | #endif 100 | 101 | TEST(InputMapTest, Deque) 102 | { 103 | auto str = std::deque{'f', 'o', 'o', 'b', 'a', 'r'}; 104 | auto buf = scn::detail::make_scan_buffer(str); 105 | static_assert( 106 | std::is_same_v< 107 | decltype(buf), 108 | scn::detail::basic_scan_forward_buffer_impl>>); 109 | EXPECT_EQ(collect(buf.get()), "foobar"); 110 | } 111 | 112 | TEST(InputMapTest, DequeSubrange) 113 | { 114 | auto str = std::deque{'f', 'o', 'o', 'b', 'a', 'r'}; 115 | auto subr = scn::ranges::subrange{str.begin(), str.end()}; 116 | auto buf = scn::detail::make_scan_buffer(subr); 117 | static_assert( 118 | std::is_same_v::iterator>>>); 121 | EXPECT_EQ(collect(buf.get()), "foobar"); 122 | } 123 | 124 | TEST(InputMapTest, File) 125 | { 126 | auto buf = scn::detail::make_scan_buffer(stdin); 127 | static_assert(std::is_same_v); 128 | } 129 | -------------------------------------------------------------------------------- /tests/unittests/istream_scanner_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | #include "wrapped_gtest.h" 21 | 22 | #include 23 | 24 | struct has_istream_operator { 25 | int i{}; 26 | 27 | friend std::istream& operator>>(std::istream& is, has_istream_operator& val) 28 | { 29 | return is >> val.i; 30 | } 31 | }; 32 | template 33 | struct scn::scanner 34 | : public scn::basic_istream_scanner {}; 35 | 36 | TEST(IstreamScannerTest, HasIstreamOperator) 37 | { 38 | auto result = scn::scan("42", "{}"); 39 | ASSERT_TRUE(result); 40 | const auto& [val] = result->values(); 41 | EXPECT_EQ(val.i, 42); 42 | } 43 | TEST(IstreamScannerTest, OtherValues) 44 | { 45 | auto result = scn::scan("123 456 789", "{} {} {}"); 47 | ASSERT_TRUE(result); 48 | const auto& [a, b, c] = result->values(); 49 | EXPECT_EQ(a.i, 123); 50 | EXPECT_EQ(b.i, 456); 51 | EXPECT_EQ(c.i, 789); 52 | } 53 | -------------------------------------------------------------------------------- /tests/unittests/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | int main(int argc, char** argv) 21 | { 22 | ::testing::InitGoogleTest(&argc, argv); 23 | return RUN_ALL_TESTS(); 24 | } 25 | -------------------------------------------------------------------------------- /tests/unittests/memory_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | TEST(ToAddressTest, Pointer) 23 | { 24 | auto i = 42; 25 | auto p = scn::detail::to_address(&i); 26 | static_assert(std::is_same_v); 27 | EXPECT_EQ(p, &i); 28 | EXPECT_EQ(*p, 42); 29 | } 30 | 31 | TEST(ToAddressTest, UniquePtr) 32 | { 33 | auto u = std::make_unique(42); 34 | auto p = scn::detail::to_address(u); 35 | static_assert(std::is_same_v); 36 | EXPECT_EQ(p, u.get()); 37 | EXPECT_EQ(*p, 42); 38 | } 39 | 40 | TEST(ToAddressTest, StringViewIterator) 41 | { 42 | auto sv = std::string_view{"42"}; 43 | auto p = scn::detail::to_address(sv.begin()); 44 | static_assert(std::is_same_v); 45 | EXPECT_EQ(p, sv.data()); 46 | EXPECT_EQ(*p, '4'); 47 | } 48 | -------------------------------------------------------------------------------- /tests/unittests/ranges_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | TEST(RangesTest, VectorSequence) 28 | { 29 | static_assert(scn::range_format_kind, char>::value == 30 | scn::range_format::sequence); 31 | 32 | auto result = scn::scan>("[123, 456]", "{}"); 33 | ASSERT_TRUE(result); 34 | EXPECT_THAT(result->value(), testing::ElementsAre(123, 456)); 35 | } 36 | 37 | TEST(RangesTest, Set) 38 | { 39 | static_assert(scn::range_format_kind, char>::value == 40 | scn::range_format::set); 41 | 42 | auto result = scn::scan>("{123, 456}", "{}"); 43 | ASSERT_TRUE(result); 44 | EXPECT_THAT(result->value(), testing::ElementsAre(123, 456)); 45 | } 46 | 47 | TEST(RangesTest, Map) 48 | { 49 | static_assert(scn::range_format_kind, char>::value == 50 | scn::range_format::map); 51 | 52 | auto result = scn::scan>("{12: 34, 56: 78}", "{}"); 53 | ASSERT_TRUE(result); 54 | EXPECT_THAT(result->value(), 55 | testing::ElementsAre(std::pair{12, 34}, std::pair{56, 78})); 56 | } 57 | -------------------------------------------------------------------------------- /tests/unittests/source_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | using ::testing::Test; 23 | 24 | template 25 | struct scan_result_helper_impl; 26 | 27 | template 28 | struct scan_result_helper_impl { 29 | using type = scn::ranges::subrange; 30 | }; 31 | template 32 | struct scan_result_helper_impl { 33 | using type = scn::ranges::dangling; 34 | }; 35 | 36 | template 37 | using scan_result_helper = scn::scan_expected, 39 | It>::type, 40 | Args...>>; 41 | 42 | TEST(SourceTest, Simple) 43 | { 44 | auto r = scn::scan("123", "{}"); 45 | ASSERT_TRUE(r); 46 | EXPECT_TRUE(r->range().empty()); 47 | EXPECT_EQ(std::get<0>(r->values()), 123); 48 | } 49 | TEST(SourceTest, TwoArgs) 50 | { 51 | auto r = scn::scan("123 3.14", "{} {}"); 52 | ASSERT_TRUE(r); 53 | EXPECT_TRUE(r->range().empty()); 54 | auto [i, d] = r->values(); 55 | EXPECT_EQ(i, 123); 56 | EXPECT_DOUBLE_EQ(d, 3.14); 57 | } 58 | 59 | TEST(SourceTest, SourceIsStringLiteral) 60 | { 61 | auto result = scn::scan("123 3.14", "{} {}"); 62 | static_assert(std::is_same_v>); 64 | ASSERT_TRUE(result); 65 | EXPECT_TRUE(result->range().empty()); 66 | auto [i, d] = result->values(); 67 | EXPECT_EQ(i, 123); 68 | EXPECT_DOUBLE_EQ(d, 3.14); 69 | } 70 | 71 | TEST(SourceTest, SourceIsStringView) 72 | { 73 | auto result = scn::scan(std::string_view{"123 3.14"}, "{} {}"); 74 | static_assert(std::is_same_v< 75 | decltype(result), 76 | scan_result_helper>); 77 | ASSERT_TRUE(result); 78 | EXPECT_TRUE(result->range().empty()); 79 | auto [i, d] = result->values(); 80 | EXPECT_EQ(i, 123); 81 | EXPECT_DOUBLE_EQ(d, 3.14); 82 | } 83 | 84 | TEST(SourceTest, SourceIsStringLvalue) 85 | { 86 | auto source = std::string{"123 3.14"}; 87 | auto result = scn::scan(source, "{} {}"); 88 | static_assert( 89 | std::is_same_v>); 91 | ASSERT_TRUE(result); 92 | EXPECT_TRUE(result->range().empty()); 93 | auto [i, d] = result->values(); 94 | EXPECT_EQ(i, 123); 95 | EXPECT_DOUBLE_EQ(d, 3.14); 96 | } 97 | 98 | TEST(SourceTest, SourceIsStringRvalue) 99 | { 100 | auto result = scn::scan(std::string{"123 3.14"}, "{} {}"); 101 | static_assert( 102 | std::is_same_v>); 104 | } 105 | -------------------------------------------------------------------------------- /tests/unittests/standalone_fwd_include_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | // empty 21 | -------------------------------------------------------------------------------- /tests/unittests/standalone_scan_include_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | // empty 21 | -------------------------------------------------------------------------------- /tests/unittests/stdin_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "wrapped_gtest.h" 23 | 24 | template 25 | static std::optional read_scn() 26 | { 27 | auto r = scn::input("{}"); 28 | if (!r) { 29 | return std::nullopt; 30 | } 31 | return r->value(); 32 | } 33 | 34 | template 35 | static std::optional read_scanf() 36 | { 37 | if constexpr (std::is_same_v) { 38 | int i{}; 39 | if (std::scanf("%d", &i) != 1) { 40 | return std::nullopt; 41 | } 42 | return i; 43 | } 44 | else if constexpr (std::is_same_v) { 45 | std::string val{}; 46 | val.resize(3); 47 | if (std::scanf(" %3c", &val[0]) != 1) { 48 | return std::nullopt; 49 | } 50 | return val; 51 | } 52 | else if constexpr (std::is_same_v) { 53 | char val{}; 54 | if (std::scanf("%c", &val) != 1) { 55 | return std::nullopt; 56 | } 57 | return val; 58 | } 59 | else { 60 | static_assert(scn::detail::dependent_false::value, ""); 61 | } 62 | } 63 | 64 | template 65 | static std::optional read_cin() 66 | { 67 | T i{}; 68 | if (!(std::cin >> i)) { 69 | return std::nullopt; 70 | } 71 | return i; 72 | } 73 | 74 | using testing::Optional; 75 | 76 | TEST(Stdin, Test) 77 | { 78 | using namespace std::string_literals; 79 | 80 | EXPECT_THAT(read_scn(), Optional(100)); 81 | EXPECT_THAT(read_scn(), Optional(101)); 82 | EXPECT_THAT(read_scanf(), Optional(102)); 83 | EXPECT_THAT(read_scn(), Optional(103)); 84 | EXPECT_THAT(read_cin(), Optional(104)); 85 | EXPECT_THAT(read_scn(), Optional(105)); 86 | 87 | EXPECT_EQ(read_scn(), std::nullopt); 88 | EXPECT_THAT(read_scn(), Optional("aaa"s)); 89 | 90 | EXPECT_EQ(read_scn(), std::nullopt); 91 | EXPECT_THAT(read_scanf(), Optional("bbb"s)); 92 | 93 | EXPECT_EQ(read_scn(), std::nullopt); 94 | EXPECT_THAT(read_cin(), Optional("ccc"s)); 95 | 96 | EXPECT_THAT(read_scn(), Optional('\n')); 97 | EXPECT_THAT(read_scn(), Optional('d')); 98 | EXPECT_THAT(read_scn(), Optional('\n')); 99 | EXPECT_THAT(read_cin(), Optional('e')); 100 | } 101 | -------------------------------------------------------------------------------- /tests/unittests/stdin_test_input.txt: -------------------------------------------------------------------------------- 1 | 100 101 102 103 104 105 aaa bbb ccc 2 | d 3 | e 4 | -------------------------------------------------------------------------------- /tests/unittests/stdin_test_runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import fnmatch 4 | import os 5 | import subprocess 6 | 7 | 8 | def find_file(pattern, path): 9 | for root, dirs, files in os.walk(path): 10 | for name in files: 11 | if fnmatch.fnmatch(name, pattern): 12 | return os.path.join(root, name) 13 | return None 14 | 15 | 16 | script_dir = os.path.abspath(os.path.dirname(__file__)) 17 | stdin_test = find_file('scn_stdin_test*', script_dir) 18 | 19 | with open(os.path.join(script_dir, 'stdin_test_input.txt'), 'r') as input_file: 20 | result = subprocess.run([stdin_test], shell=True, stdin=input_file, text=True, capture_output=True) 21 | if result.returncode != 0: 22 | print(f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}") 23 | result.check_returncode() 24 | print(f"Output:\n{result.stdout}") 25 | -------------------------------------------------------------------------------- /tests/unittests/string_view_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | #include 20 | 21 | #include "wrapped_gtest.h" 22 | 23 | TEST(StringViewTest, DefaultNarrowStringViewFromNarrowSource) 24 | { 25 | auto result = scn::scan("abc def", "{}"); 26 | ASSERT_TRUE(result); 27 | EXPECT_STREQ(result->begin(), " def"); 28 | EXPECT_EQ(result->value(), "abc"); 29 | } 30 | TEST(StringViewTest, DefaultWideStringViewFromWideSource) 31 | { 32 | auto result = scn::scan(L"abc def", L"{}"); 33 | ASSERT_TRUE(result); 34 | EXPECT_STREQ(result->begin(), L" def"); 35 | EXPECT_EQ(result->value(), L"abc"); 36 | } 37 | 38 | TEST(StringViewTest, StringPresentationNarrowStringViewFromNarrowSource) 39 | { 40 | auto result = scn::scan("abc def", "{:s}"); 41 | EXPECT_TRUE(result); 42 | EXPECT_STREQ(result->begin(), " def"); 43 | EXPECT_EQ(result->value(), "abc"); 44 | } 45 | TEST(StringViewTest, StringPresentationWideStringViewFromWideSource) 46 | { 47 | auto result = scn::scan(L"abc def", L"{:s}"); 48 | ASSERT_TRUE(result); 49 | EXPECT_STREQ(result->begin(), L" def"); 50 | EXPECT_EQ(result->value(), L"abc"); 51 | } 52 | 53 | TEST(StringViewTest, CharacterPresentationWithNoWidthCausesError) 54 | { 55 | auto result = 56 | scn::scan("abc def", scn::runtime_format("{:c}")); 57 | ASSERT_FALSE(result); 58 | EXPECT_EQ(result.error().code(), scn::scan_error::invalid_format_string); 59 | } 60 | 61 | TEST(StringViewTest, CharacterPresentationNarrowStringViewFromNarrowSource) 62 | { 63 | auto result = scn::scan("abc def", "{:.4c}"); 64 | ASSERT_TRUE(result); 65 | EXPECT_STREQ(result->begin(), "def"); 66 | EXPECT_EQ(result->value(), "abc "); 67 | } 68 | TEST(StringViewTest, CharacterPresentationWideStringViewFromWideSource) 69 | { 70 | auto result = scn::scan(L"abc def", L"{:.4c}"); 71 | ASSERT_TRUE(result); 72 | EXPECT_STREQ(result->begin(), L"def"); 73 | EXPECT_EQ(result->value(), L"abc "); 74 | } 75 | 76 | TEST(StringViewTest, CharacterSetPresentationNarrowStringViewFromNarrowSource) 77 | { 78 | auto result = scn::scan("abc def", "{:[a-z]}"); 79 | ASSERT_TRUE(result); 80 | EXPECT_STREQ(result->begin(), " def"); 81 | EXPECT_EQ(result->value(), "abc"); 82 | } 83 | TEST(StringViewTest, CharacterSetPresentationWideStringViewFromWideSource) 84 | { 85 | auto result = scn::scan(L"abc def", L"{:[a-z]}"); 86 | ASSERT_TRUE(result); 87 | EXPECT_STREQ(result->begin(), L" def"); 88 | EXPECT_EQ(result->value(), L"abc"); 89 | } 90 | 91 | TEST(StringViewTest, InvalidUtf8) 92 | { 93 | auto source = std::string_view{"\x82\xf5"}; 94 | auto result = scn::scan(source, "{:.64c}"); 95 | ASSERT_FALSE(result); 96 | EXPECT_EQ(result.error().code(), scn::scan_error::invalid_scanned_value); 97 | #if 0 98 | ASSERT_TRUE(result); 99 | EXPECT_TRUE(result->range().empty()); 100 | EXPECT_EQ(result->value(), source); 101 | #endif 102 | } 103 | 104 | TEST(StringViewTest, WonkyInput) 105 | { 106 | auto source = std::string_view{"o \U0000000f\n\n\xc3"}; 107 | auto it = source.begin(); 108 | for (int i = 0; i < 5; ++i) { 109 | if (it == source.end()) { 110 | break; 111 | } 112 | auto result = scn::scan( 113 | scn::ranges::subrange{it, source.end()}, "{:.64c}"); 114 | if (result) { 115 | it = result->begin(); 116 | } 117 | } 118 | } 119 | TEST(StringViewTest, WonkyInput2) 120 | { 121 | const char source[] = {'o', ' ', '\x0f', '\n', '\n', '\xc3'}; 122 | auto input = std::string_view{source, sizeof(source)}; 123 | 124 | auto result = scn::scan(input, "{:.64c}"); 125 | ASSERT_FALSE(result); 126 | EXPECT_EQ(result.error().code(), scn::scan_error::invalid_scanned_value); 127 | #if 0 128 | ASSERT_TRUE(result); 129 | EXPECT_TRUE(result->range().empty()); 130 | EXPECT_EQ(result->value(), input); 131 | #endif 132 | } 133 | -------------------------------------------------------------------------------- /tests/unittests/test_common.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | -------------------------------------------------------------------------------- /tests/unittests/unicode_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include "wrapped_gtest.h" 19 | 20 | #include 21 | 22 | using namespace std::string_view_literals; 23 | 24 | TEST(UnicodeTest, Utf8CpLength) 25 | { 26 | EXPECT_EQ(scn::detail::utf8_code_point_length_by_starting_code_unit('a'), 27 | 1); 28 | EXPECT_EQ(scn::detail::utf8_code_point_length_by_starting_code_unit( 29 | uint8_t{0xc4}), 30 | 2); 31 | EXPECT_EQ(scn::detail::utf8_code_point_length_by_starting_code_unit( 32 | uint8_t{0xe4}), 33 | 3); 34 | EXPECT_EQ(scn::detail::utf8_code_point_length_by_starting_code_unit( 35 | uint8_t{0xf4}), 36 | 4); 37 | EXPECT_EQ(scn::detail::utf8_code_point_length_by_starting_code_unit( 38 | uint8_t{0xa4}), 39 | 0); 40 | } 41 | 42 | TEST(UnicodeTest, Utf8Decode) 43 | { 44 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive("a"), 45 | static_cast('a')); 46 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive_valid("a"), 47 | static_cast('a')); 48 | 49 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive("ä"), 50 | char32_t{0xe4}); 51 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive_valid("ä"), 52 | char32_t{0xe4}); 53 | 54 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive("�"), 55 | char32_t{0xfffd}); 56 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive_valid("�"), 57 | char32_t{0xfffd}); 58 | 59 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive("😀"), 60 | char32_t{0x1f600}); 61 | EXPECT_EQ(scn::detail::decode_utf8_code_point_exhaustive_valid("😀"), 62 | char32_t{0x1f600}); 63 | } 64 | 65 | TEST(UnicodeTest, Utf16CpLength) 66 | { 67 | EXPECT_EQ(scn::detail::utf16_code_point_length_by_starting_code_unit( 68 | uint16_t{'a'}), 69 | 1); 70 | EXPECT_EQ(scn::detail::utf16_code_point_length_by_starting_code_unit( 71 | uint16_t{0xe4}), 72 | 1); 73 | EXPECT_EQ(scn::detail::utf16_code_point_length_by_starting_code_unit( 74 | uint16_t{0xdaaa}), 75 | 2); 76 | EXPECT_EQ(scn::detail::utf16_code_point_length_by_starting_code_unit( 77 | uint16_t{0xdeee}), 78 | 0); 79 | } 80 | -------------------------------------------------------------------------------- /tests/unittests/wrapped_gtest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Elias Kosunen 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file is a part of scnlib: 16 | // https://github.com/eliaskosunen/scnlib 17 | 18 | #include 19 | 20 | SCN_GCC_PUSH 21 | SCN_GCC_IGNORE("-Wnoexcept") 22 | SCN_GCC_IGNORE("-Wrestrict") 23 | 24 | #include 25 | 26 | SCN_GCC_POP 27 | --------------------------------------------------------------------------------