diff --git a/.reuse/templates/fkYAML.commented.jinja2 b/.reuse/templates/fkYAML.commented.jinja2 index 1466db40..282e4002 100644 --- a/.reuse/templates/fkYAML.commented.jinja2 +++ b/.reuse/templates/fkYAML.commented.jinja2 @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// {% for copyright_line in copyright_lines %} diff --git a/.reuse/templates/fkYAML_support.jinja2 b/.reuse/templates/fkYAML_support.jinja2 index c9385265..752d95ec 100644 --- a/.reuse/templates/fkYAML_support.jinja2 +++ b/.reuse/templates/fkYAML_support.jinja2 @@ -1,6 +1,6 @@ _______ __ __ __ _____ __ __ __ | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -| __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +| __| _ < \_ _/| ___ | _ | |___ version 0.3.12 |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML {% for copyright_line in copyright_lines %} diff --git a/CHANGELOG.md b/CHANGELOG.md index c96e949c..019850d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [v0.3.12](https://github.com/fktn-k/fkYAML/releases/tag/v0.3.12) (2024-09-21) + +[Full Changelog](https://github.com/fktn-k/fkYAML/compare/v0.3.11...v0.3.12) + +- Parse +.inf as the positive float infinity [\#393](https://github.com/fktn-k/fkYAML/pull/393) ([fktn-k](https://github.com/fktn-k)) +- Use \_MSVC\_LANG macro when compiled with MSVC for C++ standard detection [\#392](https://github.com/fktn-k/fkYAML/pull/392) ([fktn-k](https://github.com/fktn-k)) +- Fix detecting invalid contents of block scalar headers [\#387](https://github.com/fktn-k/fkYAML/pull/387) ([fktn-k](https://github.com/fktn-k)) + +- Use likely/unlikely if available [\#395](https://github.com/fktn-k/fkYAML/pull/395) ([fktn-k](https://github.com/fktn-k)) +- Reduce string copies in parse [\#394](https://github.com/fktn-k/fkYAML/pull/394) ([fktn-k](https://github.com/fktn-k)) +- Improve conversion from scalars to native types [\#391](https://github.com/fktn-k/fkYAML/pull/391) ([fktn-k](https://github.com/fktn-k)) +- Refactor lexical analysis [\#390](https://github.com/fktn-k/fkYAML/pull/390) ([fktn-k](https://github.com/fktn-k)) +- Refactor node attributes management [\#389](https://github.com/fktn-k/fkYAML/pull/389) ([fktn-k](https://github.com/fktn-k)) +- Add node\_type/yaml\_version\_type enum class APIs [\#388](https://github.com/fktn-k/fkYAML/pull/388) ([fktn-k](https://github.com/fktn-k)) +- Improve UTF encoding detection [\#386](https://github.com/fktn-k/fkYAML/pull/386) ([fktn-k](https://github.com/fktn-k)) + ## [v0.3.11](https://github.com/fktn-k/fkYAML/releases/tag/v0.3.11) (2024-08-24) [Full Changelog](https://github.com/fktn-k/fkYAML/compare/v0.3.10...v0.3.11) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11c7e35a..f98ab363 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.8) project( fkYAML - VERSION 0.3.11 + VERSION 0.3.12 LANGUAGES CXX) ############################################################# diff --git a/Makefile b/Makefile index 6618aa9a..185cf323 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ TOOL_SRCS = $(shell find tool -type f -name '*.cpp' | sort) # target version definition TARGET_MAJOR_VERSION := 0 TARGET_MINOR_VERSION := 3 -TARGET_PATCH_VERSION := 11 +TARGET_PATCH_VERSION := 12 TARGET_VERSION_FULL := $(TARGET_MAJOR_VERSION).$(TARGET_MINOR_VERSION).$(TARGET_PATCH_VERSION) VERSION_MACRO_FILE := include/fkYAML/detail/macros/version_macros.hpp @@ -173,15 +173,21 @@ html-coverage: lcov-coverage # Benchmark # ################# -bm-debug: +build-bm-debug: cmake -B build_bm_debug -S . -DCMAKE_BUILD_TYPE=Debug -DFK_YAML_RUN_BENCHMARK=ON cmake --build build_bm_debug --config Debug - ./build_bm_debug/tool/benchmark/benchmarker ./tool/benchmark/macos.yml > ./tool/benchmark/result_debug.log -bm-release: +bm-debug: build-bm-debug + cmake -B build_bm_debug -S . -DCMAKE_BUILD_TYPE=Debug -DFK_YAML_RUN_BENCHMARK=ON + cmake --build build_bm_debug --config Debug + ./build_bm_debug/tool/benchmark/benchmarker ./tool/benchmark/macos.yml + +build-bm-release: cmake -B build_bm_release -S . -DCMAKE_BUILD_TYPE=Release -DFK_YAML_RUN_BENCHMARK=ON cmake --build build_bm_release --config Release - ./build_bm_release/tool/benchmark/benchmarker ./tool/benchmark/macos.yml > ./tool/benchmark/result_release.log + +bm-release: build-bm-release + ./build_bm_release/tool/benchmark/benchmarker ./tool/benchmark/macos.yml ################### # Maintenance # diff --git a/README.md b/README.md index 612e4ff9..4e8e3fa2 100644 --- a/README.md +++ b/README.md @@ -92,13 +92,13 @@ On an AMD Ryzen 7 5800H @3.20GHz with g++11.4.0 in Ubuntu22.04 (WSL2), fkYAML pa | Benchmark | Release (MB/s) | | ---------------------------------- | -------------- | -| fkYAML | 40.491 | +| fkYAML | 41.051 | | libfyaml | 31.110 | | rapidyaml
(with mutable buff) | 147.221 | | rapidyaml
(with immutable buff) | 144.904 | | yaml-cpp | 7.397 | -Although [rapidyaml](https://github.com/biojppm/rapidyaml) is in general 4x faster than fkYAML as it focuses on high performance, fkYAML is 30% faster than [libfyaml](https://github.com/pantoniou/libfyaml) and also 5.4x faster than [yaml-cpp](https://github.com/jbeder/yaml-cpp). +Although [rapidyaml](https://github.com/biojppm/rapidyaml) is in general 4x faster than fkYAML as it focuses on high performance, fkYAML is 30% faster than [libfyaml](https://github.com/pantoniou/libfyaml) and also 5.5x faster than [yaml-cpp](https://github.com/jbeder/yaml-cpp). Note that, since fkYAML deserializes scalars into native booleans or integers during the parsing, the performance could be more faster in some real use cases. ## Supported compilers diff --git a/docs/examples/ex_basic_node_add_anchor_name.cpp b/docs/examples/ex_basic_node_add_anchor_name.cpp index 1dc257d3..1333fa80 100644 --- a/docs/examples/ex_basic_node_add_anchor_name.cpp +++ b/docs/examples/ex_basic_node_add_anchor_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_add_tag_name.cpp b/docs/examples/ex_basic_node_add_tag_name.cpp index 28bf5403..2ddfec7f 100644 --- a/docs/examples/ex_basic_node_add_tag_name.cpp +++ b/docs/examples/ex_basic_node_add_tag_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_alias_of.cpp b/docs/examples/ex_basic_node_alias_of.cpp index 21529e9b..ae7ae033 100644 --- a/docs/examples/ex_basic_node_alias_of.cpp +++ b/docs/examples/ex_basic_node_alias_of.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_at_basic_node.cpp b/docs/examples/ex_basic_node_at_basic_node.cpp index d7014840..18e34588 100644 --- a/docs/examples/ex_basic_node_at_basic_node.cpp +++ b/docs/examples/ex_basic_node_at_basic_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_at_compatible_type.cpp b/docs/examples/ex_basic_node_at_compatible_type.cpp index 3934e055..5646ba09 100644 --- a/docs/examples/ex_basic_node_at_compatible_type.cpp +++ b/docs/examples/ex_basic_node_at_compatible_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_begin.cpp b/docs/examples/ex_basic_node_begin.cpp index 496c43a0..77b25815 100644 --- a/docs/examples/ex_basic_node_begin.cpp +++ b/docs/examples/ex_basic_node_begin.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_boolean_type.cpp b/docs/examples/ex_basic_node_boolean_type.cpp index 5a79199c..7163baad 100644 --- a/docs/examples/ex_basic_node_boolean_type.cpp +++ b/docs/examples/ex_basic_node_boolean_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_const_iterator.cpp b/docs/examples/ex_basic_node_const_iterator.cpp index 98316b59..a5915cfc 100644 --- a/docs/examples/ex_basic_node_const_iterator.cpp +++ b/docs/examples/ex_basic_node_const_iterator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_constructor_1.cpp b/docs/examples/ex_basic_node_constructor_1.cpp index bfbafe92..00811c2d 100644 --- a/docs/examples/ex_basic_node_constructor_1.cpp +++ b/docs/examples/ex_basic_node_constructor_1.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_constructor_2.cpp b/docs/examples/ex_basic_node_constructor_2.cpp index d25bf1cd..1f45fb53 100644 --- a/docs/examples/ex_basic_node_constructor_2.cpp +++ b/docs/examples/ex_basic_node_constructor_2.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,7 +10,7 @@ #include int main() { - fkyaml::node n(fkyaml::node::node_t::INTEGER); + fkyaml::node n(fkyaml::node_type::INTEGER); std::cout << n << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_3.cpp b/docs/examples/ex_basic_node_constructor_3.cpp index 4ebc35f3..9c9d45ec 100644 --- a/docs/examples/ex_basic_node_constructor_3.cpp +++ b/docs/examples/ex_basic_node_constructor_3.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,8 +10,7 @@ #include int main() { - fkyaml::node n(fkyaml::node::node_t::BOOLEAN); - fkyaml::node n2(n); + fkyaml::node n(fkyaml::node::node_t::INTEGER); std::cout << n << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_3.output b/docs/examples/ex_basic_node_constructor_3.output index c508d536..573541ac 100644 --- a/docs/examples/ex_basic_node_constructor_3.output +++ b/docs/examples/ex_basic_node_constructor_3.output @@ -1 +1 @@ -false +0 diff --git a/docs/examples/ex_basic_node_constructor_4.cpp b/docs/examples/ex_basic_node_constructor_4.cpp index 4ebc35f3..614eb52b 100644 --- a/docs/examples/ex_basic_node_constructor_4.cpp +++ b/docs/examples/ex_basic_node_constructor_4.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -12,6 +12,6 @@ int main() { fkyaml::node n(fkyaml::node::node_t::BOOLEAN); fkyaml::node n2(n); - std::cout << n << std::endl; + std::cout << n2 << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_5.cpp b/docs/examples/ex_basic_node_constructor_5.cpp index aac95517..1eaef65c 100644 --- a/docs/examples/ex_basic_node_constructor_5.cpp +++ b/docs/examples/ex_basic_node_constructor_5.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,8 +10,8 @@ #include int main() { - double pi = 3.141592; - fkyaml::node n = pi; - std::cout << n << std::endl; + fkyaml::node n(fkyaml::node::node_t::BOOLEAN); + fkyaml::node n2(std::move(n)); + std::cout << n2 << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_5.output b/docs/examples/ex_basic_node_constructor_5.output index 41bec393..c508d536 100644 --- a/docs/examples/ex_basic_node_constructor_5.output +++ b/docs/examples/ex_basic_node_constructor_5.output @@ -1 +1 @@ -3.14159 +false diff --git a/docs/examples/ex_basic_node_constructor_6.cpp b/docs/examples/ex_basic_node_constructor_6.cpp index 4caccc3d..648b375b 100644 --- a/docs/examples/ex_basic_node_constructor_6.cpp +++ b/docs/examples/ex_basic_node_constructor_6.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,7 +10,8 @@ #include int main() { - fkyaml::node n({true, false}); + double pi = 3.141592; + fkyaml::node n = pi; std::cout << n << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_6.output b/docs/examples/ex_basic_node_constructor_6.output index 70c48721..41bec393 100644 --- a/docs/examples/ex_basic_node_constructor_6.output +++ b/docs/examples/ex_basic_node_constructor_6.output @@ -1,3 +1 @@ -- true -- false - +3.14159 diff --git a/docs/examples/ex_basic_node_constructor_7.cpp b/docs/examples/ex_basic_node_constructor_7.cpp index 528b68fa..d32dd156 100644 --- a/docs/examples/ex_basic_node_constructor_7.cpp +++ b/docs/examples/ex_basic_node_constructor_7.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,10 +10,7 @@ #include int main() { - fkyaml::node n = {true, false}; + fkyaml::node n({true, false}); std::cout << n << std::endl; - - fkyaml::node n2 = {{"foo", 1024}}; - std::cout << n2 << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_constructor_7.output b/docs/examples/ex_basic_node_constructor_7.output index ee41427a..70c48721 100644 --- a/docs/examples/ex_basic_node_constructor_7.output +++ b/docs/examples/ex_basic_node_constructor_7.output @@ -1,5 +1,3 @@ - true - false -foo: 1024 - diff --git a/docs/examples/ex_basic_node_constructor_8.cpp b/docs/examples/ex_basic_node_constructor_8.cpp new file mode 100644 index 00000000..0f2fcfa6 --- /dev/null +++ b/docs/examples/ex_basic_node_constructor_8.cpp @@ -0,0 +1,19 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include + +int main() { + fkyaml::node n = {true, false}; + std::cout << n << std::endl; + + fkyaml::node n2 = {{"foo", 1024}}; + std::cout << n2 << std::endl; + return 0; +} diff --git a/docs/examples/ex_basic_node_constructor_8.output b/docs/examples/ex_basic_node_constructor_8.output new file mode 100644 index 00000000..ee41427a --- /dev/null +++ b/docs/examples/ex_basic_node_constructor_8.output @@ -0,0 +1,5 @@ +- true +- false + +foo: 1024 + diff --git a/docs/examples/ex_basic_node_contains.cpp b/docs/examples/ex_basic_node_contains.cpp index 1c4fa25b..f563d2ef 100644 --- a/docs/examples/ex_basic_node_contains.cpp +++ b/docs/examples/ex_basic_node_contains.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_copy_assignment_operator.cpp b/docs/examples/ex_basic_node_copy_assignment_operator.cpp index 4774347c..b1fe6b1d 100644 --- a/docs/examples/ex_basic_node_copy_assignment_operator.cpp +++ b/docs/examples/ex_basic_node_copy_assignment_operator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_char_array.cpp b/docs/examples/ex_basic_node_deserialize_char_array.cpp index 100af37f..a4c51913 100644 --- a/docs/examples/ex_basic_node_deserialize_char_array.cpp +++ b/docs/examples/ex_basic_node_deserialize_char_array.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_docs_char_array.cpp b/docs/examples/ex_basic_node_deserialize_docs_char_array.cpp index 09339275..bd2d608e 100644 --- a/docs/examples/ex_basic_node_deserialize_docs_char_array.cpp +++ b/docs/examples/ex_basic_node_deserialize_docs_char_array.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_docs_file_pointer.cpp b/docs/examples/ex_basic_node_deserialize_docs_file_pointer.cpp index 0c5aa469..afb70366 100644 --- a/docs/examples/ex_basic_node_deserialize_docs_file_pointer.cpp +++ b/docs/examples/ex_basic_node_deserialize_docs_file_pointer.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_docs_iterators.cpp b/docs/examples/ex_basic_node_deserialize_docs_iterators.cpp index 7c461335..2bdb73bb 100644 --- a/docs/examples/ex_basic_node_deserialize_docs_iterators.cpp +++ b/docs/examples/ex_basic_node_deserialize_docs_iterators.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_docs_string.cpp b/docs/examples/ex_basic_node_deserialize_docs_string.cpp index 2a1d0b17..a8ad6fc1 100644 --- a/docs/examples/ex_basic_node_deserialize_docs_string.cpp +++ b/docs/examples/ex_basic_node_deserialize_docs_string.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_file_pointer.cpp b/docs/examples/ex_basic_node_deserialize_file_pointer.cpp index 39f9ed54..a41d84d5 100644 --- a/docs/examples/ex_basic_node_deserialize_file_pointer.cpp +++ b/docs/examples/ex_basic_node_deserialize_file_pointer.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_iterators.cpp b/docs/examples/ex_basic_node_deserialize_iterators.cpp index 73f3cdba..28f9b1ec 100644 --- a/docs/examples/ex_basic_node_deserialize_iterators.cpp +++ b/docs/examples/ex_basic_node_deserialize_iterators.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_deserialize_string.cpp b/docs/examples/ex_basic_node_deserialize_string.cpp index 6bdff01a..ea7d0537 100644 --- a/docs/examples/ex_basic_node_deserialize_string.cpp +++ b/docs/examples/ex_basic_node_deserialize_string.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_empty.cpp b/docs/examples/ex_basic_node_empty.cpp index 25368707..872d0704 100644 --- a/docs/examples/ex_basic_node_empty.cpp +++ b/docs/examples/ex_basic_node_empty.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_end.cpp b/docs/examples/ex_basic_node_end.cpp index 01dad692..f71e1577 100644 --- a/docs/examples/ex_basic_node_end.cpp +++ b/docs/examples/ex_basic_node_end.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_extraction_operator.cpp b/docs/examples/ex_basic_node_extraction_operator.cpp index 6e1a633c..798bd549 100644 --- a/docs/examples/ex_basic_node_extraction_operator.cpp +++ b/docs/examples/ex_basic_node_extraction_operator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_float_number_type.cpp b/docs/examples/ex_basic_node_float_number_type.cpp index 99658c9b..5ceff085 100644 --- a/docs/examples/ex_basic_node_float_number_type.cpp +++ b/docs/examples/ex_basic_node_float_number_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_anchor_name.cpp b/docs/examples/ex_basic_node_get_anchor_name.cpp index f9864edf..60f83c83 100644 --- a/docs/examples/ex_basic_node_get_anchor_name.cpp +++ b/docs/examples/ex_basic_node_get_anchor_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_tag_name.cpp b/docs/examples/ex_basic_node_get_tag_name.cpp index 7571a1d8..c97af9d0 100644 --- a/docs/examples/ex_basic_node_get_tag_name.cpp +++ b/docs/examples/ex_basic_node_get_tag_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_type.cpp b/docs/examples/ex_basic_node_get_type.cpp new file mode 100644 index 00000000..eaaa3b91 --- /dev/null +++ b/docs/examples/ex_basic_node_get_type.cpp @@ -0,0 +1,34 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +int main() { + // create YAML nodes. + fkyaml::node sequence_node = {1, 2, 3}; + fkyaml::node mapping_node = {{"foo", true}, {"bar", false}}; + fkyaml::node null_node; + fkyaml::node boolean_node = true; + fkyaml::node integer_node = 256; + fkyaml::node float_node = 3.14; + fkyaml::node string_node = "Hello, world!"; + + // query node value types by calling get_type(). + std::cout << std::boolalpha; + std::cout << (sequence_node.get_type() == fkyaml::node_type::SEQUENCE) << std::endl; + std::cout << (mapping_node.get_type() == fkyaml::node_type::MAPPING) << std::endl; + std::cout << (null_node.get_type() == fkyaml::node_type::NULL_OBJECT) << std::endl; + std::cout << (boolean_node.get_type() == fkyaml::node_type::BOOLEAN) << std::endl; + std::cout << (integer_node.get_type() == fkyaml::node_type::INTEGER) << std::endl; + std::cout << (float_node.get_type() == fkyaml::node_type::FLOAT) << std::endl; + std::cout << (string_node.get_type() == fkyaml::node_type::STRING) << std::endl; + + return 0; +} diff --git a/docs/examples/ex_basic_node_get_type.output b/docs/examples/ex_basic_node_get_type.output new file mode 100644 index 00000000..814ccfee --- /dev/null +++ b/docs/examples/ex_basic_node_get_type.output @@ -0,0 +1,7 @@ +true +true +true +true +true +true +true diff --git a/docs/examples/ex_basic_node_get_value.cpp b/docs/examples/ex_basic_node_get_value.cpp index c58b57a3..fcfec222 100644 --- a/docs/examples/ex_basic_node_get_value.cpp +++ b/docs/examples/ex_basic_node_get_value.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_value.output b/docs/examples/ex_basic_node_get_value.output index 0d4885da..1f00b91f 100644 --- a/docs/examples/ex_basic_node_get_value.output +++ b/docs/examples/ex_basic_node_get_value.output @@ -1,3 +1,3 @@ 123 foo -type_error: The target node value type is not float number type. type=string +type_error: The target node value type is not float number type. type=STRING diff --git a/docs/examples/ex_basic_node_get_value_ref.cpp b/docs/examples/ex_basic_node_get_value_ref.cpp index 9ef2cb0c..156409e1 100644 --- a/docs/examples/ex_basic_node_get_value_ref.cpp +++ b/docs/examples/ex_basic_node_get_value_ref.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_value_ref.output b/docs/examples/ex_basic_node_get_value_ref.output index 9d8e9e9b..5b77725d 100644 --- a/docs/examples/ex_basic_node_get_value_ref.output +++ b/docs/examples/ex_basic_node_get_value_ref.output @@ -1,3 +1,3 @@ 123 123 -type_error: The node value is not a mapping. type=integer +type_error: The node value is not a mapping. type=INTEGER diff --git a/docs/examples/ex_basic_node_get_yaml_version.cpp b/docs/examples/ex_basic_node_get_yaml_version.cpp index 73108098..f3ab133a 100644 --- a/docs/examples/ex_basic_node_get_yaml_version.cpp +++ b/docs/examples/ex_basic_node_get_yaml_version.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_get_yaml_version_type.cpp b/docs/examples/ex_basic_node_get_yaml_version_type.cpp new file mode 100644 index 00000000..4b04c095 --- /dev/null +++ b/docs/examples/ex_basic_node_get_yaml_version_type.cpp @@ -0,0 +1,19 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +int main() { + // A `fkyaml::node` object has `yaml_version_t::VER_1_2` by default. + fkyaml::node n; + std::cout << std::boolalpha << (n.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2) << std::endl; + + return 0; +} diff --git a/docs/examples/ex_basic_node_get_yaml_version_type.output b/docs/examples/ex_basic_node_get_yaml_version_type.output new file mode 100644 index 00000000..27ba77dd --- /dev/null +++ b/docs/examples/ex_basic_node_get_yaml_version_type.output @@ -0,0 +1 @@ +true diff --git a/docs/examples/ex_basic_node_has_anchor_name.cpp b/docs/examples/ex_basic_node_has_anchor_name.cpp index 361b0714..7ec66e0a 100644 --- a/docs/examples/ex_basic_node_has_anchor_name.cpp +++ b/docs/examples/ex_basic_node_has_anchor_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_has_tag_name.cpp b/docs/examples/ex_basic_node_has_tag_name.cpp index e958bbe6..18cc5185 100644 --- a/docs/examples/ex_basic_node_has_tag_name.cpp +++ b/docs/examples/ex_basic_node_has_tag_name.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_insertion_operator.cpp b/docs/examples/ex_basic_node_insertion_operator.cpp index b8e10bd6..334c57d2 100644 --- a/docs/examples/ex_basic_node_insertion_operator.cpp +++ b/docs/examples/ex_basic_node_insertion_operator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_integer_type.cpp b/docs/examples/ex_basic_node_integer_type.cpp index e276afa4..72416f3d 100644 --- a/docs/examples/ex_basic_node_integer_type.cpp +++ b/docs/examples/ex_basic_node_integer_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_alias.cpp b/docs/examples/ex_basic_node_is_alias.cpp index 9728f23c..3ef18d67 100644 --- a/docs/examples/ex_basic_node_is_alias.cpp +++ b/docs/examples/ex_basic_node_is_alias.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_anchor.cpp b/docs/examples/ex_basic_node_is_anchor.cpp index 4ba0470b..fd281ad1 100644 --- a/docs/examples/ex_basic_node_is_anchor.cpp +++ b/docs/examples/ex_basic_node_is_anchor.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_boolean.cpp b/docs/examples/ex_basic_node_is_boolean.cpp index 4b987a27..7c35939c 100644 --- a/docs/examples/ex_basic_node_is_boolean.cpp +++ b/docs/examples/ex_basic_node_is_boolean.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_float_number.cpp b/docs/examples/ex_basic_node_is_float_number.cpp index 54ce41b5..22d05204 100644 --- a/docs/examples/ex_basic_node_is_float_number.cpp +++ b/docs/examples/ex_basic_node_is_float_number.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_integer.cpp b/docs/examples/ex_basic_node_is_integer.cpp index 073a2993..70ad97c3 100644 --- a/docs/examples/ex_basic_node_is_integer.cpp +++ b/docs/examples/ex_basic_node_is_integer.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_mapping.cpp b/docs/examples/ex_basic_node_is_mapping.cpp index c888c83e..9d8e5346 100644 --- a/docs/examples/ex_basic_node_is_mapping.cpp +++ b/docs/examples/ex_basic_node_is_mapping.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_null.cpp b/docs/examples/ex_basic_node_is_null.cpp index e4bac289..f53de4e3 100644 --- a/docs/examples/ex_basic_node_is_null.cpp +++ b/docs/examples/ex_basic_node_is_null.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_scalar.cpp b/docs/examples/ex_basic_node_is_scalar.cpp index fe97d35f..1cf878db 100644 --- a/docs/examples/ex_basic_node_is_scalar.cpp +++ b/docs/examples/ex_basic_node_is_scalar.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -18,13 +18,13 @@ int main() { fkyaml::node float_node = 3.14; fkyaml::node string_node = "Hello, world!"; - // call type() + // check if the nodes are a scalar by calling is_scalar() std::cout << std::boolalpha; - std::cout << (null_node.type() == fkyaml::node::node_t::NULL_OBJECT) << std::endl; - std::cout << (boolean_node.type() == fkyaml::node::node_t::BOOLEAN) << std::endl; - std::cout << (integer_node.type() == fkyaml::node::node_t::INTEGER) << std::endl; - std::cout << (float_node.type() == fkyaml::node::node_t::FLOAT_NUMBER) << std::endl; - std::cout << (string_node.type() == fkyaml::node::node_t::STRING) << std::endl; + std::cout << null_node.is_scalar() << std::endl; + std::cout << boolean_node.is_scalar() << std::endl; + std::cout << integer_node.is_scalar() << std::endl; + std::cout << float_node.is_scalar() << std::endl; + std::cout << string_node.is_scalar() << std::endl; return 0; } diff --git a/docs/examples/ex_basic_node_is_sequence.cpp b/docs/examples/ex_basic_node_is_sequence.cpp index 52ef0a7b..a9fbfa6b 100644 --- a/docs/examples/ex_basic_node_is_sequence.cpp +++ b/docs/examples/ex_basic_node_is_sequence.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_is_string.cpp b/docs/examples/ex_basic_node_is_string.cpp index 6d1e877a..b9727d4c 100644 --- a/docs/examples/ex_basic_node_is_string.cpp +++ b/docs/examples/ex_basic_node_is_string.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_iterator.cpp b/docs/examples/ex_basic_node_iterator.cpp index a4c106c2..731f3f4d 100644 --- a/docs/examples/ex_basic_node_iterator.cpp +++ b/docs/examples/ex_basic_node_iterator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_mapping.cpp b/docs/examples/ex_basic_node_mapping.cpp index 1140337c..65a9ca51 100644 --- a/docs/examples/ex_basic_node_mapping.cpp +++ b/docs/examples/ex_basic_node_mapping.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_mapping_type.cpp b/docs/examples/ex_basic_node_mapping_type.cpp index 89229c7d..fc9dac88 100644 --- a/docs/examples/ex_basic_node_mapping_type.cpp +++ b/docs/examples/ex_basic_node_mapping_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_node.cpp b/docs/examples/ex_basic_node_node.cpp index bcff18f4..56d60887 100644 --- a/docs/examples/ex_basic_node_node.cpp +++ b/docs/examples/ex_basic_node_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_node_t.cpp b/docs/examples/ex_basic_node_node_t.cpp index 066ab888..023a59ec 100644 --- a/docs/examples/ex_basic_node_node_t.cpp +++ b/docs/examples/ex_basic_node_node_t.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_eq.cpp b/docs/examples/ex_basic_node_operator_eq.cpp index f1c84685..4a580fce 100644 --- a/docs/examples/ex_basic_node_operator_eq.cpp +++ b/docs/examples/ex_basic_node_operator_eq.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_ge.cpp b/docs/examples/ex_basic_node_operator_ge.cpp index 41bce864..a4f6ce4a 100644 --- a/docs/examples/ex_basic_node_operator_ge.cpp +++ b/docs/examples/ex_basic_node_operator_ge.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_gt.cpp b/docs/examples/ex_basic_node_operator_gt.cpp index ef48cb71..8d264e5d 100644 --- a/docs/examples/ex_basic_node_operator_gt.cpp +++ b/docs/examples/ex_basic_node_operator_gt.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_le.cpp b/docs/examples/ex_basic_node_operator_le.cpp index 7a001665..c21080a1 100644 --- a/docs/examples/ex_basic_node_operator_le.cpp +++ b/docs/examples/ex_basic_node_operator_le.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_lt.cpp b/docs/examples/ex_basic_node_operator_lt.cpp index fdade063..4dcb90ff 100644 --- a/docs/examples/ex_basic_node_operator_lt.cpp +++ b/docs/examples/ex_basic_node_operator_lt.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_operator_ne.cpp b/docs/examples/ex_basic_node_operator_ne.cpp index cb8a9288..feb70c83 100644 --- a/docs/examples/ex_basic_node_operator_ne.cpp +++ b/docs/examples/ex_basic_node_operator_ne.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_sequence.cpp b/docs/examples/ex_basic_node_sequence.cpp index a1e62535..097b263b 100644 --- a/docs/examples/ex_basic_node_sequence.cpp +++ b/docs/examples/ex_basic_node_sequence.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_sequence_type.cpp b/docs/examples/ex_basic_node_sequence_type.cpp index ffde35bb..3f7b4407 100644 --- a/docs/examples/ex_basic_node_sequence_type.cpp +++ b/docs/examples/ex_basic_node_sequence_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_serialize.cpp b/docs/examples/ex_basic_node_serialize.cpp index 217e4581..4f05b52c 100644 --- a/docs/examples/ex_basic_node_serialize.cpp +++ b/docs/examples/ex_basic_node_serialize.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_serialize_docs.cpp b/docs/examples/ex_basic_node_serialize_docs.cpp index 095f56b2..59a0ed31 100644 --- a/docs/examples/ex_basic_node_serialize_docs.cpp +++ b/docs/examples/ex_basic_node_serialize_docs.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_set_yaml_version.cpp b/docs/examples/ex_basic_node_set_yaml_version.cpp index 39123175..3e8b8373 100644 --- a/docs/examples/ex_basic_node_set_yaml_version.cpp +++ b/docs/examples/ex_basic_node_set_yaml_version.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_set_yaml_version_type.cpp b/docs/examples/ex_basic_node_set_yaml_version_type.cpp new file mode 100644 index 00000000..5b46ad19 --- /dev/null +++ b/docs/examples/ex_basic_node_set_yaml_version_type.cpp @@ -0,0 +1,24 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +int main() { + fkyaml::node n; + n.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_1); + fkyaml::node n2; + n2.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_2); + + std::cout << std::boolalpha; + std::cout << (n.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1) << std::endl; + std::cout << (n2.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2) << std::endl; + + return 0; +} diff --git a/docs/examples/ex_basic_node_set_yaml_version_type.output b/docs/examples/ex_basic_node_set_yaml_version_type.output new file mode 100644 index 00000000..bb101b64 --- /dev/null +++ b/docs/examples/ex_basic_node_set_yaml_version_type.output @@ -0,0 +1,2 @@ +true +true diff --git a/docs/examples/ex_basic_node_size.cpp b/docs/examples/ex_basic_node_size.cpp index 78aad3c5..1e036749 100644 --- a/docs/examples/ex_basic_node_size.cpp +++ b/docs/examples/ex_basic_node_size.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_string_type.cpp b/docs/examples/ex_basic_node_string_type.cpp index 01bdc6dd..52e52d53 100644 --- a/docs/examples/ex_basic_node_string_type.cpp +++ b/docs/examples/ex_basic_node_string_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_subscript_operator_basic_node.cpp b/docs/examples/ex_basic_node_subscript_operator_basic_node.cpp index 12225e31..981ad043 100644 --- a/docs/examples/ex_basic_node_subscript_operator_basic_node.cpp +++ b/docs/examples/ex_basic_node_subscript_operator_basic_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_subscript_operator_compatible_type.cpp b/docs/examples/ex_basic_node_subscript_operator_compatible_type.cpp index 7c33df28..59364cda 100644 --- a/docs/examples/ex_basic_node_subscript_operator_compatible_type.cpp +++ b/docs/examples/ex_basic_node_subscript_operator_compatible_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_swap_member.cpp b/docs/examples/ex_basic_node_swap_member.cpp index 783ad09e..0b12f3bf 100644 --- a/docs/examples/ex_basic_node_swap_member.cpp +++ b/docs/examples/ex_basic_node_swap_member.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_swap_std.cpp b/docs/examples/ex_basic_node_swap_std.cpp index d3867313..4109bd4f 100644 --- a/docs/examples/ex_basic_node_swap_std.cpp +++ b/docs/examples/ex_basic_node_swap_std.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_type.cpp b/docs/examples/ex_basic_node_type.cpp index 066ab888..023a59ec 100644 --- a/docs/examples/ex_basic_node_type.cpp +++ b/docs/examples/ex_basic_node_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_value_converter_type.cpp b/docs/examples/ex_basic_node_value_converter_type.cpp index 4c213402..d6ec0511 100644 --- a/docs/examples/ex_basic_node_value_converter_type.cpp +++ b/docs/examples/ex_basic_node_value_converter_type.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_basic_node_yaml_version_t.cpp b/docs/examples/ex_basic_node_yaml_version_t.cpp index b4a8e3d2..b3ce83a6 100644 --- a/docs/examples/ex_basic_node_yaml_version_t.cpp +++ b/docs/examples/ex_basic_node_yaml_version_t.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_exception_constructor_msg.cpp b/docs/examples/ex_exception_constructor_msg.cpp index 36d5c601..08aa0fb4 100644 --- a/docs/examples/ex_exception_constructor_msg.cpp +++ b/docs/examples/ex_exception_constructor_msg.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_exception_constructor_noarg.cpp b/docs/examples/ex_exception_constructor_noarg.cpp index cd4d92d0..8cf32478 100644 --- a/docs/examples/ex_exception_constructor_noarg.cpp +++ b/docs/examples/ex_exception_constructor_noarg.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_exception_what.cpp b/docs/examples/ex_exception_what.cpp index 2fb0266a..3f8ee5f1 100644 --- a/docs/examples/ex_exception_what.cpp +++ b/docs/examples/ex_exception_what.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_macros_versions.cpp b/docs/examples/ex_macros_versions.cpp index 0b794213..2012a76c 100644 --- a/docs/examples/ex_macros_versions.cpp +++ b/docs/examples/ex_macros_versions.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_node_type.cpp b/docs/examples/ex_node_type.cpp new file mode 100644 index 00000000..8373f9c5 --- /dev/null +++ b/docs/examples/ex_node_type.cpp @@ -0,0 +1,34 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +int main() { + // create YAML nodes. + fkyaml::node sequence_node = {1, 2, 3}; + fkyaml::node mapping_node = {{"foo", true}, {"bar", false}}; + fkyaml::node null_node; + fkyaml::node boolean_node = true; + fkyaml::node integer_node = 256; + fkyaml::node float_node = 3.14; + fkyaml::node string_node = "Hello, world!"; + + // query node value types by calling get_type() + std::cout << std::boolalpha; + std::cout << (sequence_node.get_type() == fkyaml::node_type::SEQUENCE) << std::endl; + std::cout << (mapping_node.get_type() == fkyaml::node_type::MAPPING) << std::endl; + std::cout << (null_node.get_type() == fkyaml::node_type::NULL_OBJECT) << std::endl; + std::cout << (boolean_node.get_type() == fkyaml::node_type::BOOLEAN) << std::endl; + std::cout << (integer_node.get_type() == fkyaml::node_type::INTEGER) << std::endl; + std::cout << (float_node.get_type() == fkyaml::node_type::FLOAT) << std::endl; + std::cout << (string_node.get_type() == fkyaml::node_type::STRING) << std::endl; + + return 0; +} diff --git a/docs/examples/ex_node_type.output b/docs/examples/ex_node_type.output new file mode 100644 index 00000000..814ccfee --- /dev/null +++ b/docs/examples/ex_node_type.output @@ -0,0 +1,7 @@ +true +true +true +true +true +true +true diff --git a/docs/examples/ex_node_value_converter_from_node.cpp b/docs/examples/ex_node_value_converter_from_node.cpp index 0ad5290b..8a647920 100644 --- a/docs/examples/ex_node_value_converter_from_node.cpp +++ b/docs/examples/ex_node_value_converter_from_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_node_value_converter_to_node.cpp b/docs/examples/ex_node_value_converter_to_node.cpp index 69cb6d0e..b5875c6a 100644 --- a/docs/examples/ex_node_value_converter_to_node.cpp +++ b/docs/examples/ex_node_value_converter_to_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_operator_literal_yaml.cpp b/docs/examples/ex_operator_literal_yaml.cpp index 78fe7704..adf62d2b 100644 --- a/docs/examples/ex_operator_literal_yaml.cpp +++ b/docs/examples/ex_operator_literal_yaml.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_at.cpp b/docs/examples/ex_ordered_map_at.cpp index 0b9ab6d7..c667ffef 100644 --- a/docs/examples/ex_ordered_map_at.cpp +++ b/docs/examples/ex_ordered_map_at.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_constructor_initializer_list.cpp b/docs/examples/ex_ordered_map_constructor_initializer_list.cpp index 4486d4dd..ebbf9f10 100644 --- a/docs/examples/ex_ordered_map_constructor_initializer_list.cpp +++ b/docs/examples/ex_ordered_map_constructor_initializer_list.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_constructor_noarg.cpp b/docs/examples/ex_ordered_map_constructor_noarg.cpp index 3f783787..ece30874 100644 --- a/docs/examples/ex_ordered_map_constructor_noarg.cpp +++ b/docs/examples/ex_ordered_map_constructor_noarg.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_emplace.cpp b/docs/examples/ex_ordered_map_emplace.cpp index 0b610320..b1aa7824 100644 --- a/docs/examples/ex_ordered_map_emplace.cpp +++ b/docs/examples/ex_ordered_map_emplace.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_find.cpp b/docs/examples/ex_ordered_map_find.cpp index 1dfcad78..29686f22 100644 --- a/docs/examples/ex_ordered_map_find.cpp +++ b/docs/examples/ex_ordered_map_find.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_ordered_map_subscript_operator.cpp b/docs/examples/ex_ordered_map_subscript_operator.cpp index 700e122c..2edf8d6f 100644 --- a/docs/examples/ex_ordered_map_subscript_operator.cpp +++ b/docs/examples/ex_ordered_map_subscript_operator.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/ex_yaml_version_type.cpp b/docs/examples/ex_yaml_version_type.cpp new file mode 100644 index 00000000..935eb444 --- /dev/null +++ b/docs/examples/ex_yaml_version_type.cpp @@ -0,0 +1,32 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +int main() { + char input[] = "%YAML 1.2\n" + "---\n" + "foo: bar\n"; + + // deserialize a YAML formatted string. + fkyaml::node n = fkyaml::node::deserialize(input); + + // query the YAML version by calling get_yaml_version_type(). + std::cout << std::boolalpha; + std::cout << (n.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2) << std::endl; + + // overwrite the YAML version to 1.1. + n.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_1); + + // query the YAML version again. + std::cout << (n.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1) << std::endl; + + return 0; +} diff --git a/docs/examples/ex_yaml_version_type.output b/docs/examples/ex_yaml_version_type.output new file mode 100644 index 00000000..bb101b64 --- /dev/null +++ b/docs/examples/ex_yaml_version_type.output @@ -0,0 +1,2 @@ +true +true diff --git a/docs/examples/tutorial_1.cpp b/docs/examples/tutorial_1.cpp index a9737943..5fd40a5e 100644 --- a/docs/examples/tutorial_1.cpp +++ b/docs/examples/tutorial_1.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/tutorial_2.cpp b/docs/examples/tutorial_2.cpp index 690fca16..0dea7d26 100644 --- a/docs/examples/tutorial_2.cpp +++ b/docs/examples/tutorial_2.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/tutorial_3.cpp b/docs/examples/tutorial_3.cpp index e4b74d85..22595568 100644 --- a/docs/examples/tutorial_3.cpp +++ b/docs/examples/tutorial_3.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/examples/tutorial_4.cpp b/docs/examples/tutorial_4.cpp index 106549ee..313869de 100644 --- a/docs/examples/tutorial_4.cpp +++ b/docs/examples/tutorial_4.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/docs/mkdocs/docs/api/basic_node/constructor.md b/docs/mkdocs/docs/api/basic_node/constructor.md index 10a094c3..b9f42a3e 100644 --- a/docs/mkdocs/docs/api/basic_node/constructor.md +++ b/docs/mkdocs/docs/api/basic_node/constructor.md @@ -5,11 +5,13 @@ ```cpp basic_node() = default; // (1) -explicit basic_node(const node_t type); // (2) +explicit basic_node(const node_type type); // (2) -basic_node(const basic_node& rhs); // (3) +explicit basic_node(const node_t type); // (3) deprecated -basic_node(basic_node&& rhs) noexcept; // (4) +basic_node(const basic_node& rhs); // (4) + +basic_node(basic_node&& rhs) noexcept; // (5) template < typename CompatibleType, typename U = detail::remove_cv_ref_t, @@ -19,14 +21,14 @@ template < detail::disjunction>>::value, int> = 0> basic_node(CompatibleType&& val) noexcept( - noexcept(ConverterType::to_node(std::declval(), std::declval()))); // (5) + noexcept(ConverterType::to_node(std::declval(), std::declval()))); // (6) template < typename NodeRefStorageType, detail::enable_if_t::value, int> = 0> -basic_node(const NodeRefStorageType& node_ref_storage) noexcept; // (6) +basic_node(const NodeRefStorageType& node_ref_storage) noexcept; // (7) -basic_node(initializer_list_t init); // (7) +basic_node(initializer_list_t init); // (8) ``` Constructs a new basic_node from a variety of data sources. @@ -54,7 +56,7 @@ The resulting basic_node has the [`node_t::NULL_OBJECT`](node_t.md) type. ## Overload (2) ```cpp -explicit basic_node(const node_t type); +explicit basic_node(const node_type type); ``` Constructs a basic_node with the given type. The resulting basic_node has a default value for the given type. @@ -77,6 +79,45 @@ The resulting basic_node has a default value for the given type. ## Overload (3) +```cpp +explicit basic_node(const node_t type); +``` + +!!! warning "Deprecation" + + The constructor `#!cpp basic_node(const node_type)` replaces the constructor `#!cpp basic_node(const node_t)` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace calls like + + ```cpp + fkyaml::node n(fkyaml::node::node_t::MAPPING); + ``` + + with + + ```cpp + fkyaml::node n(fkyaml::node_type::MAPPING); + ``` + +Constructs a basic_node with the given type. +The resulting basic_node has a default value for the given type. + +### **Parameters** + +***`type`*** [in] +: A YAML node type. + +???+ Example + + ```cpp + --8<-- "examples/ex_basic_node_constructor_3.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_basic_node_constructor_3.output" + ``` + +## Overload (4) + ```cpp basic_node(const basic_node& rhs); ``` @@ -91,15 +132,15 @@ The resulting basic_node has the same type and value as `rhs`. ???+ Example ```cpp - --8<-- "examples/ex_basic_node_constructor_3.cpp:9" + --8<-- "examples/ex_basic_node_constructor_4.cpp:9" ``` output: ```bash - --8<-- "examples/ex_basic_node_constructor_3.output" + --8<-- "examples/ex_basic_node_constructor_4.output" ``` -## Overload (4) +## Overload (5) ```cpp basic_node(basic_node&& rhs) noexcept; @@ -116,15 +157,15 @@ The value of the argument `rhs` after calling this move constructor, will be the ???+ Example ```cpp - --8<-- "examples/ex_basic_node_constructor_4.cpp:9" + --8<-- "examples/ex_basic_node_constructor_5.cpp:9" ``` output: ```bash - --8<-- "examples/ex_basic_node_constructor_4.output" + --8<-- "examples/ex_basic_node_constructor_5.output" ``` -## Overload (5) +## Overload (6) ```cpp template < @@ -156,15 +197,15 @@ The resulting basic_node has the value of `val` and the type which is associated ???+ Example ```cpp - --8<-- "examples/ex_basic_node_constructor_5.cpp:9" + --8<-- "examples/ex_basic_node_constructor_6.cpp:9" ``` output: ```bash - --8<-- "examples/ex_basic_node_constructor_5.output" + --8<-- "examples/ex_basic_node_constructor_6.output" ``` -## Overload (6) +## Overload (7) ```cpp template < @@ -194,15 +235,15 @@ The resulting basic_node has the value of the referenced basic_node by `node_ref ???+ Example ```cpp - --8<-- "examples/ex_basic_node_constructor_6.cpp:9" + --8<-- "examples/ex_basic_node_constructor_7.cpp:9" ``` output: ```bash - --8<-- "examples/ex_basic_node_constructor_6.output" + --8<-- "examples/ex_basic_node_constructor_7.output" ``` -## Overload (7) +## Overload (8) ```cpp basic_node(initializer_list_t init); @@ -220,12 +261,12 @@ If `init` contains a sequence of basic_node objects in which the number of basic ???+ Example ```cpp - --8<-- "examples/ex_basic_node_constructor_7.cpp:9" + --8<-- "examples/ex_basic_node_constructor_8.cpp:9" ``` output: ```bash - --8<-- "examples/ex_basic_node_constructor_7.output" + --8<-- "examples/ex_basic_node_constructor_8.output" ``` --- diff --git a/docs/mkdocs/docs/api/basic_node/get_type.md b/docs/mkdocs/docs/api/basic_node/get_type.md new file mode 100644 index 00000000..b8ad5cd2 --- /dev/null +++ b/docs/mkdocs/docs/api/basic_node/get_type.md @@ -0,0 +1,38 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node.hpp) + +# fkyaml::basic_node::get_type + +```cpp +node_type get_type() const noexcept; +``` + +Returns the type of the YAML node value as a value from the [`node_type`](../node_type.md) enumeration. + +### **Return Value** + +The type of the YAML node value. + +| Value Type | Return Value | +| --------------------- | ---------------------- | +| sequence | node_type::SEQUENCE | +| mapping | node_type::MAPPING | +| null | node_type::NULL_OBJECT | +| boolean | node_type::BOOLEAN | +| integer | node_type::INTEGER | +| floating point number | node_type::FLOAT | +| string | node_type::STRING | + +???+ Example + + ```cpp + --8<-- "examples/ex_basic_node_get_type.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_basic_node_get_type.output" + ``` + +### **See Also** + +* [node_type](../node_type.md) diff --git a/docs/mkdocs/docs/api/basic_node/get_yaml_version.md b/docs/mkdocs/docs/api/basic_node/get_yaml_version.md index 1dea35a4..eabc0f9b 100644 --- a/docs/mkdocs/docs/api/basic_node/get_yaml_version.md +++ b/docs/mkdocs/docs/api/basic_node/get_yaml_version.md @@ -6,6 +6,19 @@ yaml_version_t get_yaml_version() const noexcept; ``` +!!! warning "Deprecation" + + The function [`#!cpp yaml_version_type get_yaml_version_type()`](set_yaml_version_type.md) replaces the function `#!cpp basic_node::yaml_version_t get_yaml_version()` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace calls like + + ```cpp + fkyaml::node::yaml_version_t v = n.get_yaml_version(); + ``` + with + + ```cpp + fkyaml::yaml_version_type v = n.get_yaml_version_type(); + ``` + Returns the version of the YAML format applied for the `basic_node` object. ### **Return Value** diff --git a/docs/mkdocs/docs/api/basic_node/get_yaml_version_type.md b/docs/mkdocs/docs/api/basic_node/get_yaml_version_type.md new file mode 100644 index 00000000..122e836c --- /dev/null +++ b/docs/mkdocs/docs/api/basic_node/get_yaml_version_type.md @@ -0,0 +1,35 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node.hpp) + +# fkyaml::basic_node::get_yaml_version_type + +```cpp +yaml_version_type get_yaml_version_type() const noexcept; +``` + +Returns the version of the YAML format applied for the `basic_node` object. + +### **Return Value** + +The version of the YAML format applied to the basic_node object. + +| YAML version | Return Value | +| ------------ | ------------------------------ | +| 1.1 | yaml_version_type::VERSION_1_1 | +| 1.2 | yaml_version_type::VERSION_1_2 | + +???+ Example + + ```cpp + --8<-- "examples/ex_basic_node_get_yaml_version_type.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_basic_node_get_yaml_version_type.output" + ``` + +## **See Also** + +* [basic_node](index.md) +* [set_yaml_version_type](set_yaml_version_type.md) +* [yaml_verion_type](../yaml_version_type.md) diff --git a/docs/mkdocs/docs/api/basic_node/index.md b/docs/mkdocs/docs/api/basic_node/index.md index ce896c11..8b9ad0ea 100644 --- a/docs/mkdocs/docs/api/basic_node/index.md +++ b/docs/mkdocs/docs/api/basic_node/index.md @@ -34,19 +34,19 @@ This class provides features to handle YAML nodes. ## Member Types -| Name | Description | -|-------------------------------------------------|------------------------------------------------------------| -| [sequence_type](sequence_type.md) | The type used to store sequence node value containers. | -| [mapping_type](mapping_type.md) | The type used to store mapping node value containers. | -| [boolean_type](boolean_type.md) | The type used to store boolean node values. | -| [integer_type](integer_type.md) | The type used to store integer node values. | -| [float_number_type](float_number_type.md) | The type used to store float number node values. | -| [string_type](string_type.md) | The type used to store string node values. | -| [value_converter_type](value_converter_type.md) | The type used to convert between node and native data. | -| [iterator](iterator.md) | The type for non-constant iterators. | -| [const_iterator](const_iterator.md) | The type for constant iterators. | -| [node_t](node_t.md) | The type used to store the internal value type. | -| [yaml_version_t](yaml_version_t.md) | The type used to store the enable version of YAML. | +| Name | Description | +| ----------------------------------------------- | ------------------------------------------------------------------- | +| [sequence_type](sequence_type.md) | The type used to store sequence node value containers. | +| [mapping_type](mapping_type.md) | The type used to store mapping node value containers. | +| [boolean_type](boolean_type.md) | The type used to store boolean node values. | +| [integer_type](integer_type.md) | The type used to store integer node values. | +| [float_number_type](float_number_type.md) | The type used to store float number node values. | +| [string_type](string_type.md) | The type used to store string node values. | +| [value_converter_type](value_converter_type.md) | The type used to convert between node and native data. | +| [iterator](iterator.md) | The type for non-constant iterators. | +| [const_iterator](const_iterator.md) | The type for constant iterators. | +| [node_t](node_t.md) | **(DEPRECATED)** The type used to store the internal value type. | +| [yaml_version_t](yaml_version_t.md) | **(DEPRECATED)** The type used to store the enable version of YAML. | ## Member Functions @@ -61,17 +61,18 @@ This class provides features to handle YAML nodes. | [alias_of](alias_of.md) | (static) | constructs a basic_node with an anchor node. | ### Inspection for Node Value Types -| Name | Description | -|---------------------------------------|-------------------------------------------------------| -| [type](type.md) | returns the type of a node value in a basic_node. | -| [is_sequence](is_sequence.md) | checks if a basic_node has a sequence node value. | -| [is_mapping](is_mapping.md) | checks if a basic_node has a mapping node value. | -| [is_null](is_null.md) | checks if a basic_node has a null node value. | -| [is_scalar](is_scalar.md) | checks if a basic_node has a scalar node value. | -| [is_boolean](is_boolean.md) | checks if a basic_node has a boolean node value. | -| [is_integer](is_integer.md) | checks if a basic_node has an integer node value. | -| [is_float_number](is_float_number.md) | checks if a basic_node has a float number node value. | -| [is_string](is_string.md) | checks if a basic_node has a string node value. | +| Name | Description | +| ------------------------------------- | ------------------------------------------------------------------ | +| [get_type](get_type.md) | returns the type of a node value in a basic_node. | +| [type](type.md) | **(DEPRECATED)** returns the type of a node value in a basic_node. | +| [is_sequence](is_sequence.md) | checks if a basic_node has a sequence node value. | +| [is_mapping](is_mapping.md) | checks if a basic_node has a mapping node value. | +| [is_null](is_null.md) | checks if a basic_node has a null node value. | +| [is_scalar](is_scalar.md) | checks if a basic_node has a scalar node value. | +| [is_boolean](is_boolean.md) | checks if a basic_node has a boolean node value. | +| [is_integer](is_integer.md) | checks if a basic_node has an integer node value. | +| [is_float_number](is_float_number.md) | checks if a basic_node has a float number node value. | +| [is_string](is_string.md) | checks if a basic_node has a string node value. | ### Conversions | Name | | Description | @@ -114,7 +115,7 @@ This class provides features to handle YAML nodes. | [operator>](operator_gt.md) | comparison: greater than | | [operator>=](operator_ge.md) | comparison: greater than or equal | -### Aliasing Nodes +### Manipulations for Node Properties | Name | Description | | ------------------------------------- | -------------------------------------------------------- | | [is_alias](is_alias.md) | checks if a basic_node is an alias node. | @@ -122,6 +123,17 @@ This class provides features to handle YAML nodes. | [add_anchor_name](add_anchor_name.md) | registers an anchor name to a basic_node object. | | [get_anchor_name](get_anchor_name.md) | gets an anchor name associated with a basic_node object. | | [has_anchor_name](has_anchor_name.md) | checks if a basic_node has any anchor name. | +| [add_tag_name](add_tag_name.md) | registers a tag name to a basic_node object. | +| [get_tag_name](get_tag_name.md) | gets a tag name associated with a basic_node object. | +| [has_tag_name](has_tag_name.md) | checks if a basic_node has any tag name. | + +### Manipulations for Document Properties +| Name | Description | +| ------------------------------------------------- | ------------------------------------------------------------------------- | +| [get_yaml_version_type](get_yaml_version_type.md) | gets a YAML version associated with a basic_node object. | +| [set_yaml_version_type](set_yaml_version_type.md) | sets a YAML version to a basic_node object. | +| [get_yaml_version](get_yaml_version.md) | **(DEPRECATED)** gets a YAML version associated with a basic_node object. | +| [set_yaml_version](set_yaml_version.md) | **(DEPRECATED)** sets a YAML version to a basic_node object. | ### Modifiers diff --git a/docs/mkdocs/docs/api/basic_node/node_t.md b/docs/mkdocs/docs/api/basic_node/node_t.md index 2ec58f85..d337fd44 100644 --- a/docs/mkdocs/docs/api/basic_node/node_t.md +++ b/docs/mkdocs/docs/api/basic_node/node_t.md @@ -15,6 +15,20 @@ enum class node_t }; ``` +!!! warning "Deprecation" + + The enum class [`#!cpp fkyaml::node_type`](../node_type.md) replaces the type alias `#!cpp fkyaml::basic_node::node_t` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace usages like + + ```cpp + fkyaml::node::node_t t; + ``` + + with + + ```cpp + fkyaml::node_type t; + ``` + This enumeration collects the different YAML value types. They are internally used to distinguish the stored values, and the functions [`is_sequence`](is_sequence.md), [`is_mapping`](is_mapping.md) and [`is_scalar`](is_scalar.md) (with [`is_null`](is_null.md), [`is_boolean`](is_boolean.md), [`is_integer`](is_integer.md), [`is_float_number`](is_float_number.md), [`is_string`](is_string.md)) rely on it. !!! Note "Types of scalars" diff --git a/docs/mkdocs/docs/api/basic_node/set_yaml_version.md b/docs/mkdocs/docs/api/basic_node/set_yaml_version.md index 55446c0b..9090762e 100644 --- a/docs/mkdocs/docs/api/basic_node/set_yaml_version.md +++ b/docs/mkdocs/docs/api/basic_node/set_yaml_version.md @@ -6,12 +6,26 @@ void set_yaml_version(const yaml_version_t version) noexcept; ``` -Sets the version of the YAML format to the `basic_node` object. +!!! warning "Deprecation" + + The function [`#!cpp void set_yaml_version_type(const yaml_version_type)`](set_yaml_version_type.md) replaces the function `#!cpp void set_yaml_version(const basic_node::yaml_version_t)` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace calls like + + ```cpp + n.set_yaml_version(fkyaml::node::yaml_version_t::VER_1_2); + ``` + + with + + ```cpp + n.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_2); + ``` + +Sets a target YAML version to the `basic_node` object. ### **Parameters** ***version*** [in] -: A version of the YAML format. +: A target YAML version. ???+ Example diff --git a/docs/mkdocs/docs/api/basic_node/set_yaml_version_type.md b/docs/mkdocs/docs/api/basic_node/set_yaml_version_type.md new file mode 100644 index 00000000..5884d71f --- /dev/null +++ b/docs/mkdocs/docs/api/basic_node/set_yaml_version_type.md @@ -0,0 +1,31 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node.hpp) + +# fkyaml::basic_node::set_yaml_version_type + +```cpp +void set_yaml_version_type(const yaml_version_type version) noexcept; +``` + +Sets a target YAML version to the `basic_node` object. + +### **Parameters** + +***version*** [in] +: A target YAML version. + +???+ Example + + ```cpp + --8<-- "examples/ex_basic_node_set_yaml_version_type.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_basic_node_set_yaml_version_type.output" + ``` + +## **See Also** + +* [basic_node](index.md) +* [get_yaml_version_type](get_yaml_version_type.md) +* [yaml_verion_type](../yaml_version_type.md) diff --git a/docs/mkdocs/docs/api/basic_node/type.md b/docs/mkdocs/docs/api/basic_node/type.md index 727f087f..8a3f0038 100644 --- a/docs/mkdocs/docs/api/basic_node/type.md +++ b/docs/mkdocs/docs/api/basic_node/type.md @@ -6,6 +6,20 @@ node_t type() const noexcept; ``` +!!! warning "Deprecation" + + The function [`#!cpp node_type get_type()`](get_type.md) replaces the function `basic_node::node_t type()` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace calls like + + ```cpp + fkyaml::node::node_t t = n.type(); + ``` + + with + + ```cpp + fkyaml::node_type t = n.get_type(); + ``` + Returns the type of the YAML node value as a value from the [`node_t`](node_t.md) enumeration. ### **Return Value** diff --git a/docs/mkdocs/docs/api/basic_node/yaml_version_t.md b/docs/mkdocs/docs/api/basic_node/yaml_version_t.md index 5f9a6e58..ad9d6700 100644 --- a/docs/mkdocs/docs/api/basic_node/yaml_version_t.md +++ b/docs/mkdocs/docs/api/basic_node/yaml_version_t.md @@ -10,6 +10,20 @@ enum class yaml_version_t }; ``` +!!! warning "Deprecation" + + The enum class [`fkyaml::yaml_version_type`](../yaml_version_type.md) replaces the type alias `fkyaml::basic_node::yaml_version_t` which has been deprecated in version 0.3.12. It will be removed in version 0.4.0. Please replace usages like + + ```cpp + fkyaml::node::yaml_version_t v; + ``` + + with + + ```cpp + fkyaml::yaml_version_type v; + ``` + This enumeration collects the used versions of YAML specification. It is used as meta data of a basic_node and the functions [`get_yaml_version`](get_yaml_version.md) and [`set_yaml_version`](set_yaml_version.md) rely on it. ???+ Example diff --git a/docs/mkdocs/docs/api/exception/index.md b/docs/mkdocs/docs/api/exception/index.md index e540efd9..876ab06b 100644 --- a/docs/mkdocs/docs/api/exception/index.md +++ b/docs/mkdocs/docs/api/exception/index.md @@ -13,6 +13,7 @@ A basic exception class used in the fkYAML library. | Type | Description | | --------------------------------------- | ---------------------------------------------------- | | [invalid_encoding](invalid_encoding.md) | The exception indicating an encoding error. | +| [out_of_range](out_of_range.md) | The exception indicating an out-of-range error. | | [parse_error](parse_error.md) | The exception indicating an error in parsing. | | [type_error](type_error.md) | The exception indicating an invalid type conversion. | diff --git a/docs/mkdocs/docs/api/node_type.md b/docs/mkdocs/docs/api/node_type.md new file mode 100644 index 00000000..24c573de --- /dev/null +++ b/docs/mkdocs/docs/api/node_type.md @@ -0,0 +1,61 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/node_type.hpp) + +# fkyaml::node_type + +```cpp +enum class node_type : std::uint32_t +{ + SEQUENCE, + MAPPING, + NULL_OBJECT, + BOOLEAN, + INTEGER, + FLOAT, + STRING, +}; +``` + +This enumeration collects the different YAML value types. They are internally used to distinguish the stored values, and the following [basic_node](basic_node/index.md)'s functions rely on it: + +* [`is_sequence()`](basic_node/is_sequence.md) +* [`is_mapping()`](basic_node/is_mapping.md) +* [`is_scalar()`](basic_node/is_scalar.md) + * [`is_null()`](basic_node/is_null.md) + * [`is_boolean()`](basic_node/is_boolean.md) + * [`is_integer()`](basic_node/is_integer.md) + * [`is_float_number()`](basic_node/is_float_number.md) + * [`is_string()`](basic_node/is_string.md) + +!!! Note "Types of scalars" + + There are five enumerators for scalars (`NULL_OBJECT`, `BOOLEAN`, `INTEGER`, `FLOAT`, `STRING`) to distinguish between different types of scalars: + + * `std::nullptr_t` for null scalar values + * [`basic_node::boolean_type`](basic_node/boolean_type.md) for boolean scalar values + * [`basic_node::integer_type`](basic_node/integer_type.md) for integer scalar values + * [`basic_node::float_number_type`](basic_node/float_number_type.md) for float number scalar values + * [`basic_node::string_type`](basic_node/string_type.md) for string scalar values + +???+ Example + + ```cpp + --8<-- "examples/ex_node_type.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_node_type.output" + ``` + +### **See Also** + +* [basic_node](basic_node/index.md) + * [get_type](basic_node/get_type.md) + * [is_sequence](basic_node/is_sequence.md) + * [is_mapping](basic_node/is_mapping.md) + * [is_scalar](basic_node/is_scalar.md) + * [is_null](basic_node/is_null.md) + * [is_boolean](basic_node/is_boolean.md) + * [is_integer](basic_node/is_integer.md) + * [is_float_number](basic_node/is_float_number.md) + * [is_string](basic_node/is_string.md) diff --git a/docs/mkdocs/docs/api/yaml_version_type.md b/docs/mkdocs/docs/api/yaml_version_type.md new file mode 100644 index 00000000..bba81947 --- /dev/null +++ b/docs/mkdocs/docs/api/yaml_version_type.md @@ -0,0 +1,33 @@ +Defined in header [``](https://github.com/fktn-k/fkYAML/blob/develop/include/fkYAML/yaml_version_type.hpp) + +# fkyaml::yaml_version_type + +```cpp +enum class yaml_version_type +{ + VERSION_1_1, + VERSION_1_2, +}; +``` + +This enumeration collects the used versions of YAML specification. It is used as meta data of a basic_node and the following [basic_node](basic_node/index.md)'s functions rely on it: + +* [`get_yaml_version_type()`](basic_node/get_yaml_version_type.md) +* [`set_yaml_version_type()`](basic_node/set_yaml_version_type.md) + +???+ Example + + ```cpp + --8<-- "examples/ex_yaml_version_type.cpp:9" + ``` + + output: + ```bash + --8<-- "examples/ex_yaml_version_type.output" + ``` + +### **See Also** + +* [deserialize](basic_node/deserialize.md) +* [get_yaml_version_type](basic_node/get_yaml_version_type.md) +* [set_yaml_version_type](basic_node/set_yaml_version_type.md) diff --git a/docs/mkdocs/docs/home/releases.md b/docs/mkdocs/docs/home/releases.md index a7659dcf..91f9832f 100644 --- a/docs/mkdocs/docs/home/releases.md +++ b/docs/mkdocs/docs/home/releases.md @@ -1,5 +1,59 @@ # Releases +## [v0.3.12](https://github.com/fktn-k/fkYAML/releases/tag/v0.3.12) (2024-09-21) + +!!! abstract "Release Packages" + + * [fkYAML.zip](https://github.com/fktn-k/fkYAML/releases/download/v0.3.12/fkYAML.zip) + * [fkYAML.tgz](https://github.com/fktn-k/fkYAML/releases/download/v0.3.12/fkYAML.tgz) + * [fkYAML_single_header.zip](https://github.com/fktn-k/fkYAML/releases/download/v0.3.12/fkYAML_single_header.zip) + * [fkYAML_single_header.tgz](https://github.com/fktn-k/fkYAML/releases/download/v0.3.12/fkYAML_single_header.tgz) + * [node.hpp](https://github.com/fktn-k/fkYAML/releases/download/v0.3.12/node.hpp) (single header) + +### Summary + +This release adds some basic_node APIs to get/set node attributes for more secure backwards compatibilities in future releases. Because of that, some existing APIs have been deprecated (see the list down below). It's recommended for users to replace deprecated API usages with new APIs. +Furthermore, relatively large refactoring has been made for better performance and more flexible node attribute configurations (just preparation for now, but will be added within a few coming releases). As a result, deserialization performance has increased. See the Benchmarking section in README.md for details. +Last but not least, several bugs have also been resolved in deserialization, and active C++ standard is now correctly detected at compile time when using MSVC compilers where `__cplusplus` macro doesn't hold a correct value by default. + +#### :sparkles: New Features + +- Add node\_type/yaml\_version\_type enum class APIs by [fktn-k](https://github.com/fktn-k) in [\#388](https://github.com/fktn-k/fkYAML/pull/388) + - In this PR, the following APIs has been deprecated. Although they still work as before except for compile-time deprecation warnings, it's highly recommended to replace their usages with new APIs since they are planned to be removed in v0.4.0. See the Deprecation notes in each deprecated API reference page for migration guides. + - [`fkyaml::basic_node::node_t`](https://fktn-k.github.io/fkYAML/api/basic_node/node_t/) + - Replace with: [`fkyaml::node_type`](https://fktn-k.github.io/fkYAML/api/node_type/) + - [`fkyaml::basic_node::basic_node(const fkyaml::basic_node::node_t)`](https://fktn-k.github.io/fkYAML/api/basic_node/constructor/#overload-3) + - Replace with: [`fkyaml::basic_node::basic_node(const fkyaml::node_type)`](https://fktn-k.github.io/fkYAML/api/basic_node/constructor/#overload-2) + - [`fkyaml::basic_node::node_t fkyaml::basic_node::type()`](https://fktn-k.github.io/fkYAML/api/basic_node/type/) + - Replace with: [`fkyaml::basic_node::get_type()`](https://fktn-k.github.io/fkYAML/api/basic_node/get_type/) + - [`fkyaml::basic_node::yaml_version_t`](https://fktn-k.github.io/fkYAML/api/basic_node/yaml_version_t/) + - Replace with: [`fkyaml::yaml_version_type`](https://fktn-k.github.io/fkYAML/api/yaml_version_type/) + - [`fkyaml::basic_node::yaml_version_t fkyaml::basic_node::get_yaml_version()`](https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version/) + - Replace with: [`fkyaml::yaml_version_type fkyaml::basic_node::get_yaml_version_type()`](https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version_type/) + - [`void fkyaml::basic_node::set_yaml_version(const fkyaml::basic_node::yaml_version_t)`](https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version/) + - Replace with: [`void fkyaml::basic_node::set_yaml_version_type(const fkyaml::yaml_version_type)`](https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version_type/) + + +#### :zap: Improvements + +- Improve UTF encoding detection by [fktn-k](https://github.com/fktn-k) in [\#386](https://github.com/fktn-k/fkYAML/pull/386) +- Refactor node attributes management by [fktn-k](https://github.com/fktn-k) in [\#389](https://github.com/fktn-k/fkYAML/pull/389) +- Refactor lexical analysis by [fktn-k](https://github.com/fktn-k) in [\#390](https://github.com/fktn-k/fkYAML/pull/390) +- Improve conversion from scalars to native types by [fktn-k](https://github.com/fktn-k) in [\#391](https://github.com/fktn-k/fkYAML/pull/391) +- Reduce string copies in parse by [fktn-k](https://github.com/fktn-k) in [\#394](https://github.com/fktn-k/fkYAML/pull/394) +- Use likely/unlikely if available by [fktn-k](https://github.com/fktn-k) in [\#395](https://github.com/fktn-k/fkYAML/pull/395) + +#### :bug: Bug Fixes + +- Fix detecting invalid contents of block scalar headers by [fktn-k](https://github.com/fktn-k) in [\#387](https://github.com/fktn-k/fkYAML/pull/387) +- Use \_MSVC\_LANG macro when compiled with MSVC for C++ standard detection by [fktn-k](https://github.com/fktn-k) in [\#392](https://github.com/fktn-k/fkYAML/pull/392) +- Parse +.inf as the positive float infinity by [fktn-k](https://github.com/fktn-k) in [\#393](https://github.com/fktn-k/fkYAML/pull/393) + + +**Full Changelog**: https://github.com/fktn-k/fkYAML/compare/v0.3.11...v0.3.12 + +--- + ## **fkYAML version 0.3.11** !!! abstract "Release Packages" diff --git a/docs/mkdocs/docs/tutorials/cmake_integration.md b/docs/mkdocs/docs/tutorials/cmake_integration.md index 9b1972a9..da75b79e 100644 --- a/docs/mkdocs/docs/tutorials/cmake_integration.md +++ b/docs/mkdocs/docs/tutorials/cmake_integration.md @@ -57,7 +57,7 @@ Since CMake v3.11, [`FetchContent`](https://cmake.org/cmake/help/latest/module/F FetchContent_Declare( fkYAML GIT_REPOSITORY https://github.com/fktn-k/fkYAML.git - GIT_TAG v0.3.11 + GIT_TAG v0.3.12 ) FetchContent_MakeAvailable(fkYAML) diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 82960ce1..1d404255 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -55,7 +55,7 @@ markdown_extensions: pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.magiclink - - pymdownx.superfences: + - pymdownx.superfences - pymdownx.tabbed: alternate_style: true - pymdownx.tilde @@ -116,9 +116,11 @@ nav: - float_number_type: api/basic_node/float_number_type.md - get_anchor_name: api/basic_node/get_anchor_name.md - get_tag_name: api/basic_node/get_tag_name.md + - get_type: api/basic_node/get_type.md - get_value: api/basic_node/get_value.md - get_value_ref: api/basic_node/get_value_ref.md - get_yaml_version: api/basic_node/get_yaml_version.md + - get_yaml_version_type: api/basic_node/get_yaml_version_type.md - has_anchor_name: api/basic_node/has_anchor_name.md - has_tag_name: api/basic_node/has_tag_name.md - integer_type: api/basic_node/integer_type.md @@ -142,6 +144,7 @@ nav: - serialize: api/basic_node/serialize.md - serialize_docs: api/basic_node/serialize_docs.md - set_yaml_version: api/basic_node/set_yaml_version.md + - set_yaml_version_type: api/basic_node/set_yaml_version_type.md - size: api/basic_node/size.md - string_type: api/basic_node/string_type.md - swap: api/basic_node/swap.md @@ -165,6 +168,7 @@ nav: - parse_error: api/exception/parse_error.md - type_error: api/exception/type_error.md - macros: api/macros.md + - node_type: api/node_type.md - node_value_converter: - node_value_converter: api/node_value_converter/index.md - from_node: api/node_value_converter/from_node.md @@ -180,3 +184,4 @@ nav: - emplace: api/ordered_map/emplace.md - find: api/ordered_map/find.md - operator[]: api/ordered_map/operator[].md + - yaml_version_type: api/yaml_version_type.md diff --git a/fkYAML.natvis b/fkYAML.natvis index a3d2b4bb..609fe880 100644 --- a/fkYAML.natvis +++ b/fkYAML.natvis @@ -4,26 +4,26 @@ - - - {*(m_node_value.p_sequence)} - {*(m_node_value.p_mapping)} - nullptr - {m_node_value.boolean} - {m_node_value.integer} - {m_node_value.float_val} - {*(m_node_value.p_string)} + + + {*(m_node_value.p_sequence)} + {*(m_node_value.p_mapping)} + nullptr + {m_node_value.boolean} + {m_node_value.integer} + {m_node_value.float_val} + {*(m_node_value.p_string)} - + *(m_node_value.p_sequence),view(simple) - + *(m_node_value.p_mapping),view(simple) - + {second} second diff --git a/include/fkYAML/detail/assert.hpp b/include/fkYAML/detail/assert.hpp index a83b6e16..c2db086d 100644 --- a/include/fkYAML/detail/assert.hpp +++ b/include/fkYAML/detail/assert.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -15,9 +15,9 @@ #ifndef FK_YAML_ASSERT #ifndef NDEBUG #include -#define FK_YAML_ASSERT(x) assert(x) // NOLINT(cppcoreguidelines-macro-usage) +#define FK_YAML_ASSERT(x) assert(x) #else -#define FK_YAML_ASSERT(x) // NOLINT(cppcoreguidelines-macro-usage) +#define FK_YAML_ASSERT(x) #endif #endif diff --git a/include/fkYAML/detail/conversions/from_node.hpp b/include/fkYAML/detail/conversions/from_node.hpp index 5551d750..ff9e212f 100644 --- a/include/fkYAML/detail/conversions/from_node.hpp +++ b/include/fkYAML/detail/conversions/from_node.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -35,8 +35,8 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @param s A sequence node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::sequence_type& s) { - if (!n.is_sequence()) { - throw type_error("The target node value type is not sequence type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); } s = n.template get_value_ref(); } @@ -55,8 +55,8 @@ template < negation, typename BasicNodeType::sequence_type>>>::value, int> = 0> inline void from_node(const BasicNodeType& n, std::vector& s) { - if (!n.is_sequence()) { - throw type_error("The target node value is not sequence type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value is not sequence type.", n.get_type()); } s.reserve(n.size()); @@ -72,8 +72,8 @@ inline void from_node(const BasicNodeType& n, std::vector& /// @param m A mapping node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::mapping_type& m) { - if (!n.is_mapping()) { - throw type_error("The target node value type is not mapping type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_mapping()) { + throw type_error("The target node value type is not mapping type.", n.get_type()); } for (auto pair : n.template get_value_ref()) { @@ -91,8 +91,8 @@ template < has_from_node>::value, int> = 0> inline void from_node(const BasicNodeType& n, std::map& m) { - if (!n.is_mapping()) { - throw type_error("The target node value type is not mapping type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_mapping()) { + throw type_error("The target node value type is not mapping type.", n.get_type()); } for (auto pair : n.template get_value_ref()) { @@ -108,8 +108,8 @@ inline void from_node(const BasicNodeType& n, std::map::value, int> = 0> inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { // to ensure the target node value type is null. - if (!n.is_null()) { - throw type_error("The target node value type is not null type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_null()) { + throw type_error("The target node value type is not null type.", n.get_type()); } null = nullptr; } @@ -120,8 +120,8 @@ inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { /// @param b A boolean node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_type& b) { - if (!n.is_boolean()) { - throw type_error("The target node value type is not boolean type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_boolean()) { + throw type_error("The target node value type is not boolean type.", n.get_type()); } b = n.template get_value_ref(); } @@ -132,8 +132,8 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_ty /// @param i An integer node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::integer_type& i) { - if (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw type_error("The target node value type is not integer type.", n.get_type()); } i = n.template get_value_ref(); } @@ -151,17 +151,17 @@ template < negation>>::value, int> = 0> inline void from_node(const BasicNodeType& n, IntegerType& i) { - if (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw type_error("The target node value type is not integer type.", n.get_type()); } // under/overflow check. using node_int_type = typename BasicNodeType::integer_type; node_int_type tmp_int = n.template get_value_ref(); - if (tmp_int < static_cast(std::numeric_limits::min())) { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { throw exception("Integer value underflow detected."); } - if (static_cast(std::numeric_limits::max()) < tmp_int) { + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { throw exception("Integer value overflow detected."); } @@ -174,8 +174,8 @@ inline void from_node(const BasicNodeType& n, IntegerType& i) { /// @param f A float number node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::float_number_type& f) { - if (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_float_number()) { + throw type_error("The target node value type is not float number type.", n.get_type()); } f = n.template get_value_ref(); } @@ -193,15 +193,15 @@ template < negation>>::value, int> = 0> inline void from_node(const BasicNodeType& n, FloatType& f) { - if (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_float_number()) { + throw type_error("The target node value type is not float number type.", n.get_type()); } auto tmp_float = n.template get_value_ref(); - if (tmp_float < std::numeric_limits::lowest()) { + if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { throw exception("Floating point value underflow detected."); } - if (std::numeric_limits::max() < tmp_float) { + if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { throw exception("Floating point value overflow detected."); } @@ -214,8 +214,8 @@ inline void from_node(const BasicNodeType& n, FloatType& f) { /// @param s A string node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::string_type& s) { - if (!n.is_string()) { - throw type_error("The target node value type is not string type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_string()) { + throw type_error("The target node value type is not string type.", n.get_type()); } s = n.template get_value_ref(); } @@ -236,8 +236,8 @@ template < std::is_assignable>>::value, int> = 0> inline void from_node(const BasicNodeType& n, CompatibleStringType& s) { - if (!n.is_string()) { - throw type_error("The target node value type is not string type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_string()) { + throw type_error("The target node value type is not string type.", n.get_type()); } s = n.template get_value_ref(); } diff --git a/include/fkYAML/detail/conversions/from_string.hpp b/include/fkYAML/detail/conversions/from_string.hpp deleted file mode 100644 index 2a082d44..00000000 --- a/include/fkYAML/detail/conversions/from_string.hpp +++ /dev/null @@ -1,241 +0,0 @@ -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file - -#ifndef FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ -#define FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -FK_YAML_DETAIL_NAMESPACE_BEGIN - -/// @brief Convert a string YAML token to a ValueType object. -/// @tparam ValueType A target value type. -/// @tparam CharType The type of characters in a source string. -template -inline ValueType from_string(const std::basic_string& s, type_tag /*unused*/); - -/// @brief Specialization of from_string() for null values with std::string -/// @tparam N/A -template <> -inline std::nullptr_t from_string(const std::string& s, type_tag /*unused*/) { - if (s == "null" || s == "Null" || s == "NULL" || s == "~") { - return nullptr; - } - - throw exception("Cannot convert a string into a null value."); -} - -/// @brief Specialization of from_string() for boolean values with std::string. -/// @tparam N/A -template <> -inline bool from_string(const std::string& s, type_tag /*unused*/) { - if (s == "true" || s == "True" || s == "TRUE") { - return true; - } - - if (s == "false" || s == "False" || s == "FALSE") { - return false; - } - - throw exception("Cannot convert a string into a boolean value."); -} - -/// @brief Specialization of from_string() for int values with std::string. -/// @tparam N/A -template <> -inline int from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long ret = 0; - - try { - ret = std::stoi(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an int value."); - } - - return ret; -} - -/// @brief Specialization of from_string() for long values with std::string. -/// @tparam N/A -template <> -inline long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long ret = 0; - - try { - ret = std::stol(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a long value."); - } - - return ret; -} - -/// @brief Specialization of from_string() for long long values with std::string. -/// @tparam N/A -template <> -inline long long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long long ret = 0; - - try { - ret = std::stoll(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a long long value."); - } - - return ret; -} - -/// @brief Partial specialization of from_string() for other signed integer types with std::string. -/// @tparam SignedIntType A signed integer type other than long long. -template -inline enable_if_t< - conjunction< - is_non_bool_integral, std::is_signed, negation>, - negation>, negation>>::value, - SignedIntType> -from_string(const std::string& s, type_tag /*unused*/) { - const auto tmp_ret = from_string(s, type_tag {}); - if (static_cast(std::numeric_limits::max()) < tmp_ret) { - throw exception("Failed to convert a long long value into a SignedIntegerType value."); - } - - return static_cast(tmp_ret); -} - -/// @brief Specialization of from_string() for unsigned long values with std::string. -/// @tparam N/A -template <> -inline unsigned long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - unsigned long ret = 0; - - try { - ret = std::stoul(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an unsigned long value."); - } - - return ret; -} - -/// @brief Specialization of from_string() for unsigned long long values with std::string. -/// @tparam N/A -template <> -inline unsigned long long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - unsigned long long ret = 0; - - try { - ret = std::stoull(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an unsigned long long value."); - } - - return ret; -} - -/// @brief Partial specialization of from_string() for other unsigned integer types with std::string. -/// @tparam UnsignedIntType An unsigned integer type other than unsigned long long. -template -inline enable_if_t< - conjunction< - is_non_bool_integral, std::is_unsigned, - negation>, - negation>>::value, - UnsignedIntType> -from_string(const std::string& s, type_tag /*unused*/) { - const auto tmp_ret = from_string(s, type_tag {}); - if (static_cast(std::numeric_limits::max()) < tmp_ret) { - throw exception("Failed to convert an unsigned long long into an unsigned integer value."); - } - - return static_cast(tmp_ret); -} - -/// @brief Specialization of from_string() for float values with std::string. -/// @tparam N/A -template <> -inline float from_string(const std::string& s, type_tag /*unused*/) { - if (s == ".inf" || s == ".Inf" || s == ".INF") { - return std::numeric_limits::infinity(); - } - - if (s == "-.inf" || s == "-.Inf" || s == "-.INF") { - static_assert(std::numeric_limits::is_iec559, "IEEE 754 required."); - return -1 * std::numeric_limits::infinity(); - } - - if (s == ".nan" || s == ".NaN" || s == ".NAN") { - return std::nanf(""); - } - - std::size_t idx = 0; - float ret = 0.0f; - - try { - ret = std::stof(s, &idx); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a float value."); - } - - return ret; -} - -/// @brief Specialization of from_string() for double values with std::string. -/// @tparam N/A -template <> -inline double from_string(const std::string& s, type_tag /*unused*/) { - if (s == ".inf" || s == ".Inf" || s == ".INF") { - return std::numeric_limits::infinity(); - } - - if (s == "-.inf" || s == "-.Inf" || s == "-.INF") { - static_assert(std::numeric_limits::is_iec559, "IEEE 754 required."); - return -1 * std::numeric_limits::infinity(); - } - - if (s == ".nan" || s == ".NaN" || s == ".NAN") { - return std::nan(""); - } - - std::size_t idx = 0; - double ret = 0.0; - - try { - ret = std::stod(s, &idx); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a double value."); - } - - return ret; -} - -FK_YAML_DETAIL_NAMESPACE_END - -#endif /* FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ */ diff --git a/include/fkYAML/detail/conversions/scalar_conv.hpp b/include/fkYAML/detail/conversions/scalar_conv.hpp new file mode 100644 index 00000000..ae78448c --- /dev/null +++ b/include/fkYAML/detail/conversions/scalar_conv.hpp @@ -0,0 +1,834 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +// **NOTE FOR LIBARARY DEVELOPERS**: +// Implementations in this header file are intentionally optimized for conversions between YAML scalars and native C++ +// types. So, some implementations don't follow the convensions in the standard C++ functions. For example, octals must +// begin with "0o" (not "0"), which is specified in the YAML spec 1.2. + +#ifndef FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ +#define FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ + +#include +#include +#include + +#include +#include + +#if FK_YAML_HAS_TO_CHARS +// Prefer std::to_chars() and std::from_chars() functions if available. +#include +#else +// Fallback to legacy string conversion functions otherwise. +#include // std::stof(), std::stod(), std::stold() +#endif + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +////////////////////////// +// conv_limits_base // +////////////////////////// + +/// @brief A structure which provides limits for conversions between scalars and integers. +/// @note This structure contains common limits in both signed and unsigned integers. +/// @tparam NumBytes The number of bytes for the integer type. +template +struct conv_limits_base {}; + +/// @brief The specialization of conv_limits_base for 1 byte integers, e.g., int8_t, uint8_t. +template <> +struct conv_limits_base<1u> { + /// max characters for octals (0o377) without the prefix part. + static constexpr std::size_t max_chars_oct = 3; + /// max characters for hexadecimals (0xFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 2; + + /// @brief Check if the given octals are safely converted into 1 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3'); + } + + /// @brief Check if the given hexadecimals are safely converted into 1 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; + +/// @brief The specialization of conv_limits_base for 2 byte integers, e.g., int16_t, uint16_t. +template <> +struct conv_limits_base<2u> { + /// max characters for octals (0o177777) without the prefix part. + static constexpr std::size_t max_chars_oct = 6; + /// max characters for hexadecimals (0xFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 4; + + /// @brief Check if the given octals are safely converted into 2 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1'); + } + + /// @brief Check if the given hexadecimals are safely converted into 2 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; + +/// @brief The specialization of conv_limits_base for 4 byte integers, e.g., int32_t, uint32_t. +template <> +struct conv_limits_base<4u> { + /// max characters for octals (0o37777777777) without the prefix part. + static constexpr std::size_t max_chars_oct = 11; + /// max characters for hexadecimals (0xFFFFFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 8; + + /// @brief Check if the given octals are safely converted into 4 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3'); + } + + /// @brief Check if the given hexadecimals are safely converted into 4 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; + +/// @brief The specialization of conv_limits_base for 8 byte integers, e.g., int64_t, uint64_t. +template <> +struct conv_limits_base<8u> { + /// max characters for octals (0o1777777777777777777777) without the prefix part. + static constexpr std::size_t max_chars_oct = 22; + /// max characters for hexadecimals (0xFFFFFFFFFFFFFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 16; + + /// @brief Check if the given octals are safely converted into 8 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1'); + } + + /// @brief Check if the given hexadecimals are safely converted into 8 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; + +///////////////////// +// conv_limits // +///////////////////// + +/// @brief A structure which provides limits for conversions between scalars and integers. +/// @note This structure contains limits which differs based on signedness. +/// @tparam NumBytes The number of bytes for the integer type. +/// @tparam IsSigned Whether an integer is signed or unsigned +template +struct conv_limits {}; + +/// @brief The specialization of conv_limits for 1 byte signed integers, e.g., int8_t. +template <> +struct conv_limits<1u, true> : conv_limits_base<1u> { + /// with or without sign. + static constexpr bool is_signed = true; + + /// max characters for decimals (-128..127) without sign. + static constexpr std::size_t max_chars_dec = 3; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + // Making this function a static constexpr variable, a link error happens. + // Although the issue has been fixed since C++17, this workaround is necessary to let this functionality work + // with C++11 (the library's default C++ standard version). + // The same thing is applied to similar functions in the other specializations. + + static constexpr char max_value_chars[] = "127"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "128"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 1 byte unsigned integers, e.g., uint8_t. +template <> +struct conv_limits<1u, false> : conv_limits_base<1u> { + /// with or without sign. + static constexpr bool is_signed = false; + + /// max characters for decimals (0..255) without sign. + static constexpr std::size_t max_chars_dec = 3; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "255"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 2 byte signed integers, e.g., int16_t. +template <> +struct conv_limits<2u, true> : conv_limits_base<2u> { + /// with or without sign. + static constexpr bool is_signed = true; + + /// max characters for decimals (-32768..32767) without sign. + static constexpr std::size_t max_chars_dec = 5; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "32767"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "32768"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 2 byte unsigned integers, e.g., uint16_t. +template <> +struct conv_limits<2u, false> : conv_limits_base<2u> { + /// with or without sign. + static constexpr bool is_signed = false; + + /// max characters for decimals (0..65535) without sign. + static constexpr std::size_t max_chars_dec = 5; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "65535"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 4 byte signed integers, e.g., int32_t. +template <> +struct conv_limits<4u, true> : conv_limits_base<4u> { + /// with or without sign. + static constexpr bool is_signed = true; + + /// max characters for decimals (-2147483648..2147483647) without sign. + static constexpr std::size_t max_chars_dec = 10; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "2147483647"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "2147483648"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 4 byte unsigned integers, e.g., uint32_t. +template <> +struct conv_limits<4u, false> : conv_limits_base<4u> { + /// with or without sign. + static constexpr bool is_signed = false; + + /// max characters for decimals (0..4294967295) without sign. + static constexpr std::size_t max_chars_dec = 10; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "4294967295"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 8 byte signed integers, e.g., int64_t. +template <> +struct conv_limits<8u, true> : conv_limits_base<8u> { + /// with or without sign. + static constexpr bool is_signed = true; + + /// max characters for decimals (-9223372036854775808..9223372036854775807) without sign. + static constexpr std::size_t max_chars_dec = 19; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "9223372036854775807"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "9223372036854775808"; + return &min_value_chars[0]; + } +}; + +/// @brief The specialization of conv_limits for 8 byte unsigned integers, e.g., uint64_t. +template <> +struct conv_limits<8u, false> : conv_limits_base<8u> { + /// with or without sign. + static constexpr bool is_signed = false; + + /// max characters for decimals (0..18446744073709551615) without sign. + static constexpr std::size_t max_chars_dec = 20; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "18446744073709551615"; + return &max_value_chars[0]; + } + + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } +}; + +////////////////////////// +// scalar <--> null // +////////////////////////// + +/// @brief Converts a scalar into a null value +/// @tparam CharItr Type of char iterators. Its value type must be `char` (maybe cv-qualified). +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param /*unused*/ The null value holder (unused since it can only have `nullptr`) +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool aton(CharItr begin, CharItr end, std::nullptr_t& /*unused*/) noexcept { + static_assert(is_iterator_of::value, "aton() accepts iterators for char type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; + } + + uint32_t len = static_cast(std::distance(begin, end)); + + // This path is the most probable case, so check it first. + if FK_YAML_LIKELY (len == 4) { + const char* p_begin = &*begin; + return (std::strncmp(p_begin, "null", 4) == 0) || (std::strncmp(p_begin, "Null", 4) == 0) || + (std::strncmp(p_begin, "NULL", 4) == 0); + } + + if (len == 1) { + return *begin == '~'; + } + + return false; +} + +///////////////////////////// +// scalar <--> boolean // +///////////////////////////// + +/// @brief Converts a scalar into a boolean value +/// @tparam CharItr The type of char iterators. Its value type must be `char` (maybe cv-qualified). +/// @tparam BoolType The output boolean type. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param boolean The boolean value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atob(CharItr begin, CharItr end, BoolType& boolean) noexcept { + static_assert(is_iterator_of::value, "atob() accepts iterators for char type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; + } + + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + + if (len == 4) { + bool is_true_scalar = (std::strncmp(p_begin, "true", 4) == 0) || (std::strncmp(p_begin, "True", 4) == 0) || + (std::strncmp(p_begin, "TRUE", 4) == 0); + + if FK_YAML_LIKELY (is_true_scalar) { + boolean = static_cast(true); + } + return is_true_scalar; + } + + if (len == 5) { + bool is_false_scalar = (std::strncmp(p_begin, "false", 5) == 0) || (std::strncmp(p_begin, "False", 5) == 0) || + (std::strncmp(p_begin, "FALSE", 5) == 0); + + if FK_YAML_LIKELY (is_false_scalar) { + boolean = static_cast(false); + } + return is_false_scalar; + } + + return false; +} + +///////////////////////////// +// scalar <--> integer // +///////////////////////////// + +// +// scalar --> decimals +// + +/// @brief Converts a scalar into decimals. This is common implementation for both signed/unsigned integer types. +/// @warning +/// This function does NOT care about overflows if IntType is unsigned. The source string value must be validated +/// beforehand by calling either atoi_dec_pos() or atoi_dec_neg() functions. +/// Furthermore, `p_begin` and `p_end` must NOT be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_unchecked(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, + "atoi_dec_unchecked() accepts non-boolean integral types as an output type"); + + i = 0; + do { + char c = *p_begin; + if FK_YAML_UNLIKELY (c < '0' || '9' < c) { + return false; + } + // Overflow is intentional when the IntType is signed. + i = i * IntType(10) + IntType(c - '0'); + } while (++p_begin != p_end); + + return true; +} + +/// @brief Converts a scalar into positive decimals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_pos(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_dec_pos() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; + } + + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) { + // Overflow will happen. + return false; + } + + if (len == conv_limits_type::max_chars_dec) { + const char* p_max_value_chars_dec = conv_limits_type::max_value_chars_dec(); + + for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) { + if (p_begin[idx] < p_max_value_chars_dec[idx]) { + // No need to check the lower digits. Overflow will no longer happen. + break; + } + + if FK_YAML_UNLIKELY (p_begin[idx] > p_max_value_chars_dec[idx]) { + // Overflow will happen. + return false; + } + } + } + + return atoi_dec_unchecked(p_begin, p_end, i); +} + +/// @brief Converts a scalar into negative decimals. This function executes bounds check to avoid underflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It must be signed. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_neg(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_dec_neg() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; + } + + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) { + // Underflow will happen. + return false; + } + + if (len == conv_limits_type::max_chars_dec) { + const char* p_min_value_chars_dec = conv_limits_type::min_value_chars_dec(); + + for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) { + if (p_begin[idx] < p_min_value_chars_dec[idx]) { + // No need to check the lower digits. Underflow will no longer happen. + break; + } + + if FK_YAML_UNLIKELY (p_begin[idx] > p_min_value_chars_dec[idx]) { + // Underflow will happen. + return false; + } + } + } + + return atoi_dec_unchecked(p_begin, p_end, i); +} + +// +// scalar --> octals +// + +/// @brief Converts a scalar into octals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_oct(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_oct() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; + } + + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (!conv_limits_type::check_if_octs_safe(p_begin, len)) { + return false; + } + + i = 0; + do { + char c = *p_begin; + if FK_YAML_UNLIKELY (c < '0' || '7' < c) { + return false; + } + i = i * IntType(8) + IntType(c - '0'); + } while (++p_begin != p_end); + + return true; +} + +// +// scalar --> hexadecimals +// + +/// @brief Converts a scalar into hexadecimals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_hex(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_hex() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; + } + + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (!conv_limits_type::check_if_hexs_safe(p_begin, len)) { + return false; + } + + i = 0; + do { + char c = *p_begin; + IntType ci = 0; + if ('0' <= c && c <= '9') { + ci = IntType(c - '0'); + } + else if ('A' <= c && c <= 'F') { + ci = IntType(c - 'A' + 10); + } + else if ('a' <= c && c <= 'f') { + ci = IntType(c - 'a' + 10); + } + else { + return false; + } + i = i * IntType(16) + ci; + } while (++p_begin != p_end); + + return true; +} + +// +// atoi() & itoa() +// + +/// @brief Converts a scalar into integers. This function executes bounds check to avoid overflow/underflow. +/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified). +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi(CharItr begin, CharItr end, IntType& i) noexcept { + static_assert(is_iterator_of::value, "atoi() accepts iterators for char type"); + static_assert(is_non_bool_integral::value, "atoi() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; + } + + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + const char* p_end = p_begin + len; + + char first = *begin; + if (first == '+') { + return atoi_dec_pos(p_begin + 1, p_end, i); + } + + if (first == '-') { + if (!std::numeric_limits::is_signed) { + return false; + } + + bool success = atoi_dec_neg(p_begin + 1, p_end, i); + if (success) { + i *= IntType(-1); + } + + return success; + } + + if (first != '0') { + return atoi_dec_pos(p_begin, p_end, i); + } + else if (p_begin + 1 != p_end) { + switch (*(p_begin + 1)) { + case 'o': + return atoi_oct(p_begin + 2, p_end, i); + case 'x': + return atoi_hex(p_begin + 2, p_end, i); + default: + // The YAML spec doesn't allow decimals starting with 0. + return false; + } + } + + i = 0; + return true; +} + +/////////////////////////// +// scalar <--> float // +/////////////////////////// + +/// @brief Set an infinite `float` value based on the given signedness. +/// @param f The output `float` value holder. +/// @param sign Whether the infinite value should be positive or negative. +inline void set_infinity(float& f, const float sign) noexcept { + f = std::numeric_limits::infinity() * sign; +} + +/// @brief Set an infinite `double` value based on the given signedness. +/// @param f The output `double` value holder. +/// @param sign Whether the infinite value should be positive or negative. +inline void set_infinity(double& f, const double sign) noexcept { + f = std::numeric_limits::infinity() * sign; +} + +/// @brief Set a NaN `float` value. +/// @param f The output `float` value holder. +inline void set_nan(float& f) noexcept { + f = std::nanf(""); +} + +/// @brief Set a NaN `double` value. +/// @param f The output `double` value holder. +inline void set_nan(double& f) noexcept { + f = std::nan(""); +} + +#if FK_YAML_HAS_TO_CHARS + +/// @brief Converts a scalar into a floating point value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output floating point value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atof_impl(const char* p_begin, const char* p_end, FloatType& f) noexcept { + static_assert(std::is_floating_point_v, "atof_impl() accepts floating point types as an output type"); + if (auto [ptr, ec] = std::from_chars(p_begin, p_end, f); ec == std::errc {}) { + return ptr == p_end; + } + return false; +} + +#else + +/// @brief Converts a scalar into a `float` value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output `float` value holder. +/// @return true if the conversion completes successfully, false otherwise. +inline bool atof_impl(const char* p_begin, const char* p_end, float& f) { + std::size_t idx = 0; + f = std::stof(std::string(p_begin, p_end), &idx); + return idx == static_cast(p_end - p_begin); +} + +/// @brief Converts a scalar into a `double` value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output `double` value holder. +/// @return true if the conversion completes successfully, false otherwise. +inline bool atof_impl(const char* p_begin, const char* p_end, double& f) { + std::size_t idx = 0; + f = std::stod(std::string(p_begin, p_end), &idx); + return idx == static_cast(p_end - p_begin); +} + +#endif // FK_YAML_HAS_TO_CHARS + +/// @brief Converts a scalar into a floating point value. +/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified). +/// @tparam FloatType The output floatint point value type. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param f The output floating point value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atof(CharItr begin, CharItr end, FloatType& f) noexcept(noexcept(atof_impl(&*begin, &*begin, f))) { + static_assert(is_iterator_of::value, "atof() accepts iterators for char type"); + static_assert(std::is_floating_point::value, "atof() accepts floating point types as an output type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; + } + + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + const char* p_end = p_begin + len; + + if (*p_begin == '-' || *p_begin == '+') { + if (len == 5) { + const char* p_from_second = p_begin + 1; + bool is_inf_scalar = (std::strncmp(p_from_second, ".inf", 4) == 0) || + (std::strncmp(p_from_second, ".Inf", 4) == 0) || + (std::strncmp(p_from_second, ".INF", 4) == 0); + + if (is_inf_scalar) { + set_infinity(f, *p_begin == '-' ? FloatType(-1.) : FloatType(1.)); + return true; + } + } + } + else if (len == 4) { + bool is_inf_scalar = (std::strncmp(p_begin, ".inf", 4) == 0) || (std::strncmp(p_begin, ".Inf", 4) == 0) || + (std::strncmp(p_begin, ".INF", 4) == 0); + bool is_nan_scalar = false; + if (!is_inf_scalar) { + is_nan_scalar = (std::strncmp(p_begin, ".nan", 4) == 0) || (std::strncmp(p_begin, ".NaN", 4) == 0) || + (std::strncmp(p_begin, ".NAN", 4) == 0); + } + + if (is_inf_scalar) { + set_infinity(f, FloatType(1.)); + return true; + } + + if (is_nan_scalar) { + set_nan(f); + return true; + } + } + +#if FK_YAML_HAS_TO_CHARS + return atof_impl(p_begin, p_end, f); +#else + bool success = false; + try { + success = atof_impl(p_begin, p_end, f); + } + catch (const std::exception& /*unused*/) { + success = false; + } + + return success; +#endif +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ */ diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index df7cb11c..1a70fbc5 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -14,10 +14,11 @@ #include #include -#include #include #include #include +#include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -28,23 +29,23 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @brief The external constructor template for basic_node objects. /// @note All the non-specialized instanciations results in compilation error since such instantiations are not /// supported. -/// @warning All the specialization must call n.m_node_value.destroy(n.m_node_type) first in construct function to avoid +/// @warning All the specialization must call n.m_node_value.destroy() first in the construct function to avoid /// memory leak. -/// @tparam node_t The resulting YAMK node value type. -template +/// @tparam node_type The resulting YAMK node value type. +template struct external_node_constructor; /// @brief The specialization of external_node_constructor for sequence nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue sequence. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param s A lvalue sequence value. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::sequence_type& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::SEQUENCE; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::seq_bit; n.m_node_value.p_sequence = BasicNodeType::template create_object(s); } @@ -54,8 +55,8 @@ struct external_node_constructor { /// @param s A rvalue sequence value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::sequence_type&& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::SEQUENCE; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::seq_bit; n.m_node_value.p_sequence = BasicNodeType::template create_object(std::move(s)); } @@ -63,15 +64,15 @@ struct external_node_constructor { /// @brief The specialization of external_node_constructor for mapping nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue mapping. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param m A lvalue mapping value. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::mapping_type& m) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::MAPPING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::map_bit; n.m_node_value.p_mapping = BasicNodeType::template create_object(m); } @@ -81,8 +82,8 @@ struct external_node_constructor { /// @param m A rvalue mapping value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::mapping_type&& m) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::MAPPING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::map_bit; n.m_node_value.p_mapping = BasicNodeType::template create_object(std::move(m)); } @@ -90,75 +91,75 @@ struct external_node_constructor { /// @brief The specialization of external_node_constructor for null nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with nullptr. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param (unused) nullptr template ::value, int> = 0> static void construct(BasicNodeType& n, std::nullptr_t /*unused*/) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::NULL_OBJECT; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::null_bit; n.m_node_value.p_mapping = nullptr; } }; /// @brief The specialization of external_node_constructor for boolean scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with boolean. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param b A boolean value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::BOOLEAN; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::bool_bit; n.m_node_value.boolean = b; } }; /// @brief The specialization of external_node_constructor for integer scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with integers. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param i An integer value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::integer_type i) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::INTEGER; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::int_bit; n.m_node_value.integer = i; } }; /// @brief The specialization of external_node_constructor for float number scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with floating point numbers. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param f A floating point number. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::float_number_type f) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::FLOAT_NUMBER; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::float_bit; n.m_node_value.float_val = f; } }; /// @brief The specialization of external_node_constructor for string scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue strings. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param s A constant lvalue string. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::string_type& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(s); } @@ -168,8 +169,8 @@ struct external_node_constructor { /// @param s A rvalue string. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(std::move(s)); } @@ -187,8 +188,8 @@ struct external_node_constructor { negation>>::value, int> = 0> static void construct(BasicNodeType& n, const CompatibleStringType& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(s); } }; @@ -210,7 +211,7 @@ template < std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& s) noexcept { - external_node_constructor::construct(n, std::forward(s)); + external_node_constructor::construct(n, std::forward(s)); } /// @brief to_node function for BasicNodeType::mapping_type objects. @@ -225,7 +226,7 @@ template < is_basic_node, std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& m) noexcept { - external_node_constructor::construct(n, std::forward(m)); + external_node_constructor::construct(n, std::forward(m)); } /// @brief to_node function for null objects. @@ -235,7 +236,7 @@ template < typename BasicNodeType, typename NullType, enable_if_t, std::is_same>::value, int> = 0> inline void to_node(BasicNodeType& n, NullType /*unused*/) { - external_node_constructor::construct(n, nullptr); + external_node_constructor::construct(n, nullptr); } /// @brief to_node function for BasicNodeType::boolean_type objects. @@ -249,7 +250,7 @@ template < conjunction, std::is_same>::value, int> = 0> inline void to_node(BasicNodeType& n, T b) noexcept { - external_node_constructor::construct(n, b); + external_node_constructor::construct(n, b); } /// @brief to_node function for integers. @@ -261,7 +262,7 @@ template < typename BasicNodeType, typename T, enable_if_t, is_non_bool_integral>::value, int> = 0> inline void to_node(BasicNodeType& n, T i) noexcept { - external_node_constructor::construct(n, i); + external_node_constructor::construct(n, i); } /// @brief to_node function for floating point numbers. @@ -273,7 +274,7 @@ template < typename BasicNodeType, typename T, enable_if_t, std::is_floating_point>::value, int> = 0> inline void to_node(BasicNodeType& n, T f) noexcept { - external_node_constructor::construct(n, f); + external_node_constructor::construct(n, f); } /// @brief to_node function for compatible strings. @@ -289,7 +290,7 @@ template < std::is_constructible>::value, int> = 0> inline void to_node(BasicNodeType& n, const T& s) { - external_node_constructor::construct(n, s); + external_node_constructor::construct(n, s); } /// @brief to_node function for rvalue string node values @@ -298,7 +299,7 @@ inline void to_node(BasicNodeType& n, const T& s) { /// @param s An rvalue string node value. template ::value, int> = 0> inline void to_node(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - external_node_constructor::construct(n, std::move(s)); + external_node_constructor::construct(n, std::move(s)); } /// @brief A function object to call to_node functions. diff --git a/include/fkYAML/detail/conversions/to_string.hpp b/include/fkYAML/detail/conversions/to_string.hpp index 00e9ebc0..824395a6 100644 --- a/include/fkYAML/detail/conversions/to_string.hpp +++ b/include/fkYAML/detail/conversions/to_string.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/document_metainfo.hpp b/include/fkYAML/detail/document_metainfo.hpp index 7c842cc2..814f9cbe 100644 --- a/include/fkYAML/detail/document_metainfo.hpp +++ b/include/fkYAML/detail/document_metainfo.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -16,7 +16,7 @@ #include #include -#include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -24,7 +24,7 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN template ::value>> struct document_metainfo { /// The YAML version used for the YAML document. - yaml_version_t version {yaml_version_t::VER_1_2}; + yaml_version_type version {yaml_version_type::VERSION_1_2}; /// Whether or not the YAML version has been specified. bool is_version_specified {false}; /// The prefix of the primary handle. diff --git a/include/fkYAML/detail/encodings/encode_detector.hpp b/include/fkYAML/detail/encodings/encode_detector.hpp deleted file mode 100644 index 0dd3abe6..00000000 --- a/include/fkYAML/detail/encodings/encode_detector.hpp +++ /dev/null @@ -1,245 +0,0 @@ -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file - -#ifndef FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ -#define FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ - -#include -#include - -#include -#include -#include - -FK_YAML_DETAIL_NAMESPACE_BEGIN - -/// @brief Detect an encoding type for UTF-8 expected inputs. -/// @note This function doesn't support the case where the first character is null. -/// @param[in] bytes 4 bytes of an input character sequence. -/// @param[out] has_bom Whether or not the input contains a BOM. -/// @return A detected encoding type. -inline utf_encode_t detect_encoding_type(const std::array& bytes, bool& has_bom) noexcept { - has_bom = false; - - // Check if a BOM exists. - - if (bytes[0] == uint8_t(0xEFu) && bytes[1] == uint8_t(0xBBu) && bytes[2] == uint8_t(0xBFu)) { - has_bom = true; - return utf_encode_t::UTF_8; - } - - if (bytes[0] == 0 && bytes[1] == 0 && bytes[2] == uint8_t(0xFEu) && bytes[3] == uint8_t(0xFFu)) { - has_bom = true; - return utf_encode_t::UTF_32BE; - } - - if (bytes[0] == uint8_t(0xFFu) && bytes[1] == uint8_t(0xFEu) && bytes[2] == 0 && bytes[3] == 0) { - has_bom = true; - return utf_encode_t::UTF_32LE; - } - - if (bytes[0] == uint8_t(0xFEu) && bytes[1] == uint8_t(0xFFu)) { - has_bom = true; - return utf_encode_t::UTF_16BE; - } - - if (bytes[0] == uint8_t(0xFFu) && bytes[1] == uint8_t(0xFEu)) { - has_bom = true; - return utf_encode_t::UTF_16LE; - } - - // Test the first character assuming it's an ASCII character. - - if (bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0 && 0 < bytes[3] && bytes[3] < uint8_t(0x80u)) { - return utf_encode_t::UTF_32BE; - } - - if (0 < bytes[0] && bytes[0] < uint8_t(0x80u) && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0) { - return utf_encode_t::UTF_32LE; - } - - if (bytes[0] == 0 && 0 < bytes[1] && bytes[1] < uint8_t(0x80u)) { - return utf_encode_t::UTF_16BE; - } - - if (0 < bytes[0] && bytes[0] < uint8_t(0x80u) && bytes[1] == 0) { - return utf_encode_t::UTF_16LE; - } - - return utf_encode_t::UTF_8; -} - -/// @brief Detects the encoding type of the input, and consumes a BOM if it exists. -/// @tparam ItrType Type of iterators for the input. -/// @tparam ElemSize The size of one input element. -/// @param begin The beginning of input iterators. -/// @param end The end of input iterators. -/// @return A detected encoding type. -template ())))> -inline utf_encode_t detect_encoding_and_skip_bom(ItrType& begin, const ItrType& end) { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - switch (ElemSize) { - case sizeof(char): { // this case covers char8_t as well when compiled with C++20 or better. - for (int i = 0; i < 4 && begin + i != end; i++) { - bytes[i] = uint8_t(begin[i]); - } - - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - - if (has_bom) { - // skip reading the BOM. - switch (encode_type) { - case utf_encode_t::UTF_8: - std::advance(begin, 3); - break; - case utf_encode_t::UTF_16BE: - case utf_encode_t::UTF_16LE: - std::advance(begin, 2); - break; - case utf_encode_t::UTF_32BE: - case utf_encode_t::UTF_32LE: - std::advance(begin, 4); - break; - } - } - - return encode_type; - } - case sizeof(char16_t): { - if (begin == end) { - return utf_encode_t::UTF_16BE; - } - for (int i = 0; i < 2 && begin + i != end; i++) { - bytes[i * 2] = uint8_t((begin[i] & 0xFF00u) >> 8); - bytes[i * 2 + 1] = uint8_t(begin[i] & 0xFFu); - } - - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - - if (encode_type != utf_encode_t::UTF_16BE && encode_type != utf_encode_t::UTF_16LE) { - throw exception("char16_t characters must be encoded in the UTF-16 format."); - } - - if (has_bom) { - // skip reading the BOM. - std::advance(begin, 1); - } - - return encode_type; - } - case sizeof(char32_t): { - if (begin == end) { - return utf_encode_t::UTF_32BE; - } - - bytes[0] = uint8_t((*begin & 0xFF000000u) >> 24); - bytes[1] = uint8_t((*begin & 0x00FF0000u) >> 16); - bytes[2] = uint8_t((*begin & 0x0000FF00u) >> 8); - bytes[3] = uint8_t(*begin & 0x000000FFu); - - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - - if (encode_type != utf_encode_t::UTF_32BE && encode_type != utf_encode_t::UTF_32LE) { - throw exception("char32_t characters must be encoded in the UTF-32 format."); - } - - if (has_bom) { - // skip reading the BOM. - std::advance(begin, 1); - } - - return encode_type; - } - default: - throw exception("Unknown char size."); - } -} - -inline utf_encode_t detect_encoding_and_skip_bom(std::FILE* file) noexcept { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - for (int i = 0; i < 4; i++) { - char byte = 0; - std::size_t size = std::fread(&byte, sizeof(char), 1, file); - if (size != sizeof(char)) { - break; - } - bytes[i] = uint8_t(byte & 0xFF); - } - - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - - // move back to the beginning if a BOM doesn't exist. - long offset = 0; - if (has_bom) { - switch (encode_type) { - case utf_encode_t::UTF_8: - offset = 3; - break; - case utf_encode_t::UTF_16BE: - case utf_encode_t::UTF_16LE: - offset = 2; - break; - case utf_encode_t::UTF_32BE: - case utf_encode_t::UTF_32LE: - offset = 4; - break; - } - } - fseek(file, offset, SEEK_SET); - - return encode_type; -} - -inline utf_encode_t detect_encoding_and_skip_bom(std::istream& is) noexcept { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - for (int i = 0; i < 4; i++) { - char ch = 0; - is.read(&ch, 1); - std::streamsize size = is.gcount(); - if (size != 1) { - // without this, seekg() fails in the switch-case statement below. - is.clear(); - break; - } - bytes[i] = uint8_t(ch & 0xFF); - } - - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - - // move back to the beginning if a BOM doesn't exist. - std::streamoff offset = 0; - if (has_bom) { - switch (encode_type) { - case utf_encode_t::UTF_8: - offset = 3; - break; - case utf_encode_t::UTF_16BE: - case utf_encode_t::UTF_16LE: - offset = 2; - break; - case utf_encode_t::UTF_32BE: - case utf_encode_t::UTF_32LE: - offset = 4; - break; - } - } - is.seekg(offset, std::ios_base::beg); - - return encode_type; -} - -FK_YAML_DETAIL_NAMESPACE_END - -#endif /* FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ */ diff --git a/include/fkYAML/detail/encodings/uri_encoding.hpp b/include/fkYAML/detail/encodings/uri_encoding.hpp index 349cdd61..2859fad1 100644 --- a/include/fkYAML/detail/encodings/uri_encoding.hpp +++ b/include/fkYAML/detail/encodings/uri_encoding.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -25,12 +25,12 @@ class uri_encoding { /// @param begin An iterator to the first element of the character sequence. /// @param end An iterator to the past-the-end element of the character sequence. /// @return true if all the characters are valid, false otherwise. - static bool validate(std::string::const_iterator begin, std::string::const_iterator end) noexcept { + static bool validate(const char* begin, const char* end) noexcept { if (begin == end) { return true; } - std::string::const_iterator current = begin; + const char* current = begin; for (; current != end; ++current) { if (*current == '%') { @@ -56,7 +56,7 @@ class uri_encoding { /// @param begin An iterator to the first octet. /// @param end An iterator to the past-the-end element of the whole character sequence. /// @return true if the octets are valid, false otherwise. - static bool validate_octets(std::string::const_iterator& begin, std::string::const_iterator& end) { + static bool validate_octets(const char*& begin, const char*& end) { for (int i = 0; i < 2; i++, ++begin) { if (begin == end) { return false; diff --git a/include/fkYAML/detail/encodings/utf_encode_detector.hpp b/include/fkYAML/detail/encodings/utf_encode_detector.hpp new file mode 100644 index 00000000..37932607 --- /dev/null +++ b/include/fkYAML/detail/encodings/utf_encode_detector.hpp @@ -0,0 +1,337 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ +#define FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ + +#include +#include + +#include +#include +#include +#include +#include + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Detect an encoding type for UTF-8 expected inputs. +/// @note This function doesn't support the case where the first character is null. +/// @param[in] bytes 4 bytes of an input character sequence. +/// @param[out] has_bom Whether or not the input contains a BOM. +/// @return A detected encoding type. +inline utf_encode_t detect_encoding_type(const std::array& bytes, bool& has_bom) noexcept { + has_bom = false; + + uint8_t byte0 = bytes[0]; + uint8_t byte1 = bytes[1]; + uint8_t byte2 = bytes[2]; + uint8_t byte3 = bytes[3]; + + // Check if a BOM exists. + + if (byte0 == uint8_t(0xEFu) && byte1 == uint8_t(0xBBu) && byte2 == uint8_t(0xBFu)) { + has_bom = true; + return utf_encode_t::UTF_8; + } + + if (byte0 == 0 && byte1 == 0 && byte2 == uint8_t(0xFEu) && byte3 == uint8_t(0xFFu)) { + has_bom = true; + return utf_encode_t::UTF_32BE; + } + + if (byte0 == uint8_t(0xFFu) && byte1 == uint8_t(0xFEu) && byte2 == 0 && byte3 == 0) { + has_bom = true; + return utf_encode_t::UTF_32LE; + } + + if (byte0 == uint8_t(0xFEu) && byte1 == uint8_t(0xFFu)) { + has_bom = true; + return utf_encode_t::UTF_16BE; + } + + if (byte0 == uint8_t(0xFFu) && byte1 == uint8_t(0xFEu)) { + has_bom = true; + return utf_encode_t::UTF_16LE; + } + + // Test the first character assuming it's an ASCII character. + + if (byte0 == 0 && byte1 == 0 && byte2 == 0 && 0 < byte3 && byte3 < uint8_t(0x80u)) { + return utf_encode_t::UTF_32BE; + } + + if (0 < byte0 && byte0 < uint8_t(0x80u) && byte1 == 0 && byte2 == 0 && byte3 == 0) { + return utf_encode_t::UTF_32LE; + } + + if (byte0 == 0 && 0 < byte1 && byte1 < uint8_t(0x80u)) { + return utf_encode_t::UTF_16BE; + } + + if (0 < byte0 && byte0 < uint8_t(0x80u) && byte1 == 0) { + return utf_encode_t::UTF_16LE; + } + + return utf_encode_t::UTF_8; +} + +/// @brief A class which detects UTF encoding type and the existence of a BOM at the beginning. +/// @tparam ItrType Type of iterators for the input. +template +struct utf_encode_detector {}; + +/// @brief The partial specialization of utf_encode_detector for char iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) noexcept { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_8; + } + + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4 && begin + i != end; i++) { + bytes[i] = uint8_t(begin[i]); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + if (has_bom) { + // skip reading the BOM. + switch (encode_type) { + case utf_encode_t::UTF_8: + std::advance(begin, 3); + break; + case utf_encode_t::UTF_16BE: + case utf_encode_t::UTF_16LE: + std::advance(begin, 2); + break; + case utf_encode_t::UTF_32BE: + case utf_encode_t::UTF_32LE: + std::advance(begin, 4); + break; + } + } + + return encode_type; + } +}; + +#if FK_YAML_HAS_CHAR8_T + +/// @brief The partial specialization of utf_encode_detector for char8_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_8; + } + + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4 && begin + i != end; i++) { + bytes[i] = uint8_t(begin[i]); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_8) { + throw exception("char8_t characters must be encoded in the UTF-8 format."); + } + + if (has_bom) { + // skip reading the BOM. + std::advance(begin, 3); + } + + return encode_type; + } +}; + +#endif // FK_YAML_HAS_CHAR8_T + +/// @brief The partial specialization of utf_encode_detector for char16_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_16BE; + } + + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 2 && begin + i != end; i++) { + char16_t elem = begin[i]; + bytes[i * 2] = uint8_t((elem & 0xFF00u) >> 8); + bytes[i * 2 + 1] = uint8_t(elem & 0xFFu); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_16BE && encode_type != utf_encode_t::UTF_16LE) { + throw exception("char16_t characters must be encoded in the UTF-16 format."); + } + + if (has_bom) { + // skip reading the BOM. + std::advance(begin, 1); + } + + return encode_type; + } +}; + +/// @brief The partial specialization of utf_encode_detector for char32_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_32BE; + } + + std::array bytes {}; + char32_t elem = *begin; + bytes[0] = uint8_t((elem & 0xFF000000u) >> 24); + bytes[1] = uint8_t((elem & 0x00FF0000u) >> 16); + bytes[2] = uint8_t((elem & 0x0000FF00u) >> 8); + bytes[3] = uint8_t(elem & 0x000000FFu); + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_32BE && encode_type != utf_encode_t::UTF_32LE) { + throw exception("char32_t characters must be encoded in the UTF-32 format."); + } + + if (has_bom) { + // skip reading the BOM. + std::advance(begin, 1); + } + + return encode_type; + } +}; + +/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file. +struct file_utf_encode_detector { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param p_file The input file handle. + /// @return A detected encoding type. + static utf_encode_t detect(std::FILE* p_file) noexcept { + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4; i++) { + char byte = 0; + std::size_t size = std::fread(&byte, sizeof(char), 1, p_file); + if (size != sizeof(char)) { + break; + } + bytes[i] = uint8_t(byte & 0xFF); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + // move back to the beginning if a BOM doesn't exist. + long offset = 0; + if (has_bom) { + switch (encode_type) { + case utf_encode_t::UTF_8: + offset = 3; + break; + case utf_encode_t::UTF_16BE: + case utf_encode_t::UTF_16LE: + offset = 2; + break; + case utf_encode_t::UTF_32BE: + case utf_encode_t::UTF_32LE: + offset = 4; + break; + } + } + std::fseek(p_file, offset, SEEK_SET); + + return encode_type; + } +}; + +/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file. +struct stream_utf_encode_detector { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param p_file The input file handle. + /// @return A detected encoding type. + static utf_encode_t detect(std::istream& is) noexcept { + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4; i++) { + char ch = 0; + is.read(&ch, 1); + std::streamsize size = is.gcount(); + if (size != 1) { + // without this, seekg() will fail. + is.clear(); + break; + } + bytes[i] = uint8_t(ch & 0xFF); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + // move back to the beginning if a BOM doesn't exist. + std::streamoff offset = 0; + if (has_bom) { + switch (encode_type) { + case utf_encode_t::UTF_8: + offset = 3; + break; + case utf_encode_t::UTF_16BE: + case utf_encode_t::UTF_16LE: + offset = 2; + break; + case utf_encode_t::UTF_32BE: + case utf_encode_t::UTF_32LE: + offset = 4; + break; + } + } + is.seekg(offset, std::ios_base::beg); + + return encode_type; + } +}; + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ */ diff --git a/include/fkYAML/detail/encodings/utf_encode_t.hpp b/include/fkYAML/detail/encodings/utf_encode_t.hpp index ec1b9ae6..850e2ca8 100644 --- a/include/fkYAML/detail/encodings/utf_encode_t.hpp +++ b/include/fkYAML/detail/encodings/utf_encode_t.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/encodings/utf_encodings.hpp b/include/fkYAML/detail/encodings/utf_encodings.hpp index 04725a59..f1938367 100644 --- a/include/fkYAML/detail/encodings/utf_encodings.hpp +++ b/include/fkYAML/detail/encodings/utf_encodings.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/encodings/yaml_escaper.hpp b/include/fkYAML/detail/encodings/yaml_escaper.hpp index 41b90330..cc57ccce 100644 --- a/include/fkYAML/detail/encodings/yaml_escaper.hpp +++ b/include/fkYAML/detail/encodings/yaml_escaper.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -24,7 +24,7 @@ class yaml_escaper { using iterator = ::std::string::const_iterator; public: - static bool unescape(iterator& begin, iterator end, std::string& buff) { + static bool unescape(const char*& begin, const char* end, std::string& buff) { FK_YAML_ASSERT(*begin == '\\' && std::distance(begin, end) > 0); bool ret = true; @@ -81,7 +81,7 @@ class yaml_escaper { case 'x': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 1, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -89,7 +89,7 @@ class yaml_escaper { case 'u': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 2, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -97,7 +97,7 @@ class yaml_escaper { case 'U': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 4, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -111,7 +111,7 @@ class yaml_escaper { return ret; } - static ::std::string escape(iterator begin, iterator end, bool& is_escaped) { + static ::std::string escape(const char* begin, const char* end, bool& is_escaped) { ::std::string escaped {}; escaped.reserve(std::distance(begin, end)); for (; begin != end; ++begin) { @@ -310,7 +310,7 @@ class yaml_escaper { return false; } - static bool extract_codepoint(iterator& begin, iterator end, int bytes_to_read, char32_t& codepoint) { + static bool extract_codepoint(const char*& begin, const char* end, int bytes_to_read, char32_t& codepoint) { bool has_enough_room = static_cast(std::distance(begin, end)) >= (bytes_to_read - 1); if (!has_enough_room) { return false; diff --git a/include/fkYAML/detail/input/deserializer.hpp b/include/fkYAML/detail/input/deserializer.hpp index ebc32cfd..fc56734e 100644 --- a/include/fkYAML/detail/input/deserializer.hpp +++ b/include/fkYAML/detail/input/deserializer.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -12,18 +12,19 @@ #define FK_YAML_DETAIL_INPUT_DESERIALIZER_HPP_ #include -#include #include -#include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -37,19 +38,25 @@ class basic_deserializer { static_assert(is_basic_node::value, "basic_deserializer only accepts basic_node<...>"); /** A type for the target basic_node. */ - using node_type = BasicNodeType; + using basic_node_type = BasicNodeType; /** A type for the lexical analyzer. */ - using lexer_type = lexical_analyzer; + using lexer_type = lexical_analyzer; /** A type for the document metainfo. */ - using doc_metainfo_type = document_metainfo; + using doc_metainfo_type = document_metainfo; /** A type for the tag resolver. */ - using tag_resolver_type = tag_resolver; + using tag_resolver_type = tag_resolver; /** A type for sequence node value containers. */ - using sequence_type = typename node_type::sequence_type; + using sequence_type = typename basic_node_type::sequence_type; /** A type for mapping node value containers. */ - using mapping_type = typename node_type::mapping_type; + using mapping_type = typename basic_node_type::mapping_type; + /** A type for boolean node values. */ + using boolean_type = typename basic_node_type::boolean_type; + /** A type for integer node values. */ + using integer_type = typename basic_node_type::integer_type; + /** A type for floating point node values. */ + using float_number_type = typename basic_node_type::float_number_type; /** A type for string node values. */ - using string_type = typename node_type::string_type; + using string_type = typename basic_node_type::string_type; /// @brief Definition of state types of parse contexts. enum class context_state_t { @@ -74,7 +81,7 @@ class basic_deserializer { /// @param _indent The indentation width in the current line. (count from zero) /// @param _state The parse context type. /// @param _p_node The underlying node associated to this context. - parse_context(uint32_t _line, uint32_t _indent, context_state_t _state, node_type* _p_node) + parse_context(uint32_t _line, uint32_t _indent, context_state_t _state, basic_node_type* _p_node) : line(_line), indent(_indent), state(_state), @@ -101,7 +108,7 @@ class basic_deserializer { /// The parse context type. context_state_t state {context_state_t::BLOCK_MAPPING}; /// The pointer to the associated node to this context. - node_type* p_node {nullptr}; + basic_node_type* p_node {nullptr}; }; /// @brief Definitions of state types for expected flow token hints. @@ -122,22 +129,26 @@ class basic_deserializer { /// prefer the `deserialize_docs()` function. /// @tparam InputAdapterType The type of an input adapter object. /// @param input_adapter An input adapter object for the input source buffer. - /// @return node_type A root YAML node deserialized from the source string. + /// @return basic_node_type A root YAML node deserialized from the source string. template ::value, int> = 0> - node_type deserialize(InputAdapterType&& input_adapter) { + basic_node_type deserialize(InputAdapterType&& input_adapter) { + str_view input_view = input_adapter.get_buffer_view(); + lexer_type lexer(input_view); + lexical_token_t type {lexical_token_t::END_OF_BUFFER}; - lexer_type lexer(std::forward(input_adapter)); return deserialize_document(lexer, type); } /// @brief Deserialize multiple YAML documents into YAML nodes. /// @tparam InputAdapterType The type of an adapter object. /// @param input_adapter An input adapter object for the input source buffer. - /// @return std::vector Root YAML nodes for deserialized YAML documents. + /// @return std::vector Root YAML nodes for deserialized YAML documents. template ::value, int> = 0> - std::vector deserialize_docs(InputAdapterType&& input_adapter) { - lexer_type lexer(std::forward(input_adapter)); - std::vector nodes {}; + std::vector deserialize_docs(InputAdapterType&& input_adapter) { + str_view input_view = input_adapter.get_buffer_view(); + lexer_type lexer(input_view); + + std::vector nodes {}; lexical_token_t type {lexical_token_t::END_OF_BUFFER}; do { @@ -151,24 +162,24 @@ class basic_deserializer { /// @brief Deserialize a YAML document into a YAML node. /// @param lexer The lexical analyzer to be used. /// @param last_type The variable to store the last lexical token type. - /// @return node_type A root YAML node deserialized from the YAML document. - node_type deserialize_document(lexer_type& lexer, lexical_token_t& last_type) { - lexical_token_t type {lexical_token_t::END_OF_BUFFER}; + /// @return basic_node_type A root YAML node deserialized from the YAML document. + basic_node_type deserialize_document(lexer_type& lexer, lexical_token_t& last_type) { + lexical_token token {}; - node_type root; + basic_node_type root; mp_meta = root.mp_meta; // parse directives first. - deserialize_directives(lexer, type); + deserialize_directives(lexer, token); // parse node properties for root node if any uint32_t line = lexer.get_lines_processed(); uint32_t indent = lexer.get_last_token_begin_pos(); - bool found_props = deserialize_node_properties(lexer, type, line, indent); + bool found_props = deserialize_node_properties(lexer, token, line, indent); - switch (type) { + switch (token.type) { case lexical_token_t::SEQUENCE_BLOCK_PREFIX: { - root = node_type::sequence(); + root = basic_node_type::sequence(); apply_directive_set(root); if (found_props) { // If node properties are found before the block sequence entry prefix, the properties belong to the @@ -178,29 +189,29 @@ class basic_deserializer { parse_context context( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::BLOCK_SEQUENCE, &root); m_context_stack.emplace_back(std::move(context)); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; } case lexical_token_t::SEQUENCE_FLOW_BEGIN: ++m_flow_context_depth; - root = node_type::sequence(); + root = basic_node_type::sequence(); apply_directive_set(root); apply_node_properties(root); m_context_stack.emplace_back( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_SEQUENCE, &root); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; case lexical_token_t::MAPPING_FLOW_BEGIN: ++m_flow_context_depth; - root = node_type::mapping(); + root = basic_node_type::mapping(); apply_directive_set(root); apply_node_properties(root); m_context_stack.emplace_back( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_MAPPING, &root); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; default: { - root = node_type::mapping(); + root = basic_node_type::mapping(); apply_directive_set(root); if (found_props && line < lexer.get_lines_processed()) { // If node properties and a followed node are on the different line, the properties belong to the root @@ -217,7 +228,7 @@ class basic_deserializer { mp_current_node = &root; // parse YAML nodes recursively - deserialize_node(lexer, type, last_type); + deserialize_node(lexer, token, last_type); FK_YAML_ASSERT( last_type == lexical_token_t::END_OF_BUFFER || last_type == lexical_token_t::END_OF_DIRECTIVES || last_type == lexical_token_t::END_OF_DOCUMENT); @@ -237,15 +248,15 @@ class basic_deserializer { /// @brief Deserializes the YAML directives if specified. /// @param lexer The lexical analyzer to be used. /// @param last_type The variable to store the last lexical token type. - void deserialize_directives(lexer_type& lexer, lexical_token_t& last_type) { + void deserialize_directives(lexer_type& lexer, lexical_token& last_token) { bool lacks_end_of_directives_marker = false; for (;;) { - lexical_token_t type = lexer.get_next_token(); + lexical_token token = lexer.get_next_token(); - switch (type) { + switch (token.type) { case lexical_token_t::YAML_VER_DIRECTIVE: - if (mp_meta->is_version_specified) { + if FK_YAML_UNLIKELY (mp_meta->is_version_specified) { throw parse_error( "YAML version cannot be specified more than once.", lexer.get_lines_processed(), @@ -257,36 +268,41 @@ class basic_deserializer { lacks_end_of_directives_marker = true; break; case lexical_token_t::TAG_DIRECTIVE: { - const std::string& tag_handle = lexer.get_tag_handle(); - switch (tag_handle.size()) { - case 1: { + str_view tag_handle_view = lexer.get_tag_handle(); + switch (tag_handle_view.size()) { + case 1 /* ! */: { bool is_already_specified = !mp_meta->primary_handle_prefix.empty(); - if (is_already_specified) { + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "Primary handle cannot be specified more than once.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - mp_meta->primary_handle_prefix = lexer.get_tag_prefix(); + str_view tag_prefix = lexer.get_tag_prefix(); + mp_meta->primary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end()); lacks_end_of_directives_marker = true; break; } - case 2: { + case 2 /* !! */: { bool is_already_specified = !mp_meta->secondary_handle_prefix.empty(); - if (is_already_specified) { + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "Secondary handle cannot be specified more than once.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - mp_meta->secondary_handle_prefix = lexer.get_tag_prefix(); + str_view tag_prefix = lexer.get_tag_prefix(); + mp_meta->secondary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end()); lacks_end_of_directives_marker = true; break; } - default: { + default /* !! */: { + std::string tag_handle(tag_handle_view.begin(), tag_handle_view.end()); + str_view tag_prefix_view = lexer.get_tag_prefix(); + std::string tag_prefix(tag_prefix_view.begin(), tag_prefix_view.end()); bool is_already_specified = - !(mp_meta->named_handle_map.emplace(tag_handle, lexer.get_tag_prefix()).second); - if (is_already_specified) { + !(mp_meta->named_handle_map.emplace(std::move(tag_handle), std::move(tag_prefix)).second); + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "The same named handle cannot be specified more than once.", lexer.get_lines_processed(), @@ -305,14 +321,14 @@ class basic_deserializer { lacks_end_of_directives_marker = false; break; default: - if (lacks_end_of_directives_marker) { + if FK_YAML_UNLIKELY (lacks_end_of_directives_marker) { throw parse_error( "The end of directives marker (---) is missing after directives.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } // end the parsing of directives if the other tokens are found. - last_type = type; + last_token = token; return; } } @@ -321,13 +337,13 @@ class basic_deserializer { /// @brief Deserializes the YAML nodes recursively. /// @param lexer The lexical analyzer to be used. /// @param first_type The first lexical token type. - void deserialize_node(lexer_type& lexer, lexical_token_t first_type, lexical_token_t& last_type) { - lexical_token_t type = first_type; + void deserialize_node(lexer_type& lexer, const lexical_token& first_token, lexical_token_t& last_type) { + lexical_token token = first_token; uint32_t line = lexer.get_lines_processed(); uint32_t indent = lexer.get_last_token_begin_pos(); do { - switch (type) { + switch (token.type) { case lexical_token_t::EXPLICIT_KEY_PREFIX: { uint32_t pop_num = 0; if (indent == 0) { @@ -351,7 +367,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } - if (mp_current_node->is_null()) { + if FK_YAML_UNLIKELY (mp_current_node->is_null()) { // This path is needed in case the input contains nested explicit keys like the following YAML // snippet: // ```yaml @@ -359,21 +375,21 @@ class basic_deserializer { // : bar // : baz // ``` - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); } if (m_context_stack.back().state == context_state_t::BLOCK_SEQUENCE) { sequence_type& seq = mp_current_node->template get_value_ref(); - seq.emplace_back(node_type::mapping()); + seq.emplace_back(basic_node_type::mapping()); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, &(seq.back())); } - type = lexer.get_next_token(); - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + token = lexer.get_next_token(); + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event - m_context_stack.emplace_back( - line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new node_type(node_t::SEQUENCE)); + basic_node_type* p_node = new basic_node_type(node_type::SEQUENCE); + m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, p_node); mp_current_node = m_context_stack.back().p_node; apply_directive_set(*mp_current_node); parse_context context( @@ -387,7 +403,7 @@ class basic_deserializer { // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event m_context_stack.emplace_back( - line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new node_type()); + line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new basic_node_type()); mp_current_node = m_context_stack.back().p_node; apply_directive_set(*mp_current_node); indent = lexer.get_last_token_begin_pos(); @@ -397,7 +413,7 @@ class basic_deserializer { } case lexical_token_t::KEY_SEPARATOR: { bool is_empty_seq = mp_current_node->is_sequence() && mp_current_node->empty(); - if (is_empty_seq) { + if FK_YAML_UNLIKELY (is_empty_seq) { throw parse_error("sequence key should not be empty.", line, indent); } @@ -405,11 +421,11 @@ class basic_deserializer { uint32_t old_indent = indent; uint32_t old_line = line; - type = lexer.get_next_token(); + token = lexer.get_next_token(); line = lexer.get_lines_processed(); indent = lexer.get_last_token_begin_pos(); - bool found_props = deserialize_node_properties(lexer, type, line, indent); + bool found_props = deserialize_node_properties(lexer, token, line, indent); if (found_props && line == lexer.get_lines_processed()) { // defer applying node properties for the subsequent node on the same line. continue; @@ -442,7 +458,7 @@ class basic_deserializer { // ^ // this !!str tag overwrites the preceeding !!map tag. // ``` - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); @@ -450,9 +466,9 @@ class basic_deserializer { } } - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { // a key separator preceeding block sequence entries - *mp_current_node = node_type::sequence(); + *mp_current_node = basic_node_type::sequence(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); auto& cur_context = m_context_stack.back(); @@ -460,11 +476,11 @@ class basic_deserializer { cur_context.indent = indent; cur_context.state = context_state_t::BLOCK_SEQUENCE; - type = lexer.get_next_token(); + token = lexer.get_next_token(); line = lexer.get_lines_processed(); indent = lexer.get_last_token_begin_pos(); - bool has_props = deserialize_node_properties(lexer, type, line, indent); + bool has_props = deserialize_node_properties(lexer, token, line, indent); if (has_props) { uint32_t line_after_props = lexer.get_lines_processed(); if (line == line_after_props) { @@ -485,7 +501,7 @@ class basic_deserializer { line = line_after_props; indent = lexer.get_last_token_begin_pos(); mp_current_node->template get_value_ref().emplace_back( - node_type::mapping()); + basic_node_type::mapping()); mp_current_node = &mp_current_node->template get_value_ref().back(); m_context_stack.emplace_back( line_after_props, indent, context_state_t::BLOCK_MAPPING, mp_current_node); @@ -506,15 +522,16 @@ class basic_deserializer { m_context_stack.pop_back(); } - node_type key_node = std::move(*m_context_stack.back().p_node); + basic_node_type key_node = std::move(*m_context_stack.back().p_node); m_context_stack.pop_back(); - m_context_stack.back().p_node->template get_value_ref().emplace(key_node, node_type()); + m_context_stack.back().p_node->template get_value_ref().emplace( + key_node, basic_node_type()); mp_current_node = &(m_context_stack.back().p_node->operator[](std::move(key_node))); m_context_stack.emplace_back( line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_VALUE, mp_current_node); - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { - *mp_current_node = node_type::sequence(); + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + *mp_current_node = basic_node_type::sequence(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node); @@ -530,7 +547,7 @@ class basic_deserializer { break; case lexical_token_t::ANCHOR_PREFIX: case lexical_token_t::TAG_PREFIX: - deserialize_node_properties(lexer, type, line, indent); + deserialize_node_properties(lexer, token, line, indent); // Skip updating the current indent to avoid stacking a wrong indentation. // Note that node properties for block sequences as a mapping value are processed when a // `lexical_token_t::KEY_SEPARATOR` token is processed. @@ -544,7 +561,7 @@ class basic_deserializer { case lexical_token_t::SEQUENCE_BLOCK_PREFIX: { bool is_further_nested = m_context_stack.back().indent < indent; if (is_further_nested) { - mp_current_node->template get_value_ref().emplace_back(node_type::sequence()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::sequence()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node); apply_directive_set(*mp_current_node); @@ -584,7 +601,7 @@ class basic_deserializer { } }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -598,7 +615,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("Flow sequence begininng is found without separated with a comma.", line, indent); } @@ -607,7 +624,7 @@ class basic_deserializer { switch (m_context_stack.back().state) { case context_state_t::BLOCK_SEQUENCE: case context_state_t::FLOW_SEQUENCE: - mp_current_node->template get_value_ref().emplace_back(node_type::sequence()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::sequence()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::FLOW_SEQUENCE, mp_current_node); break; @@ -615,11 +632,11 @@ class basic_deserializer { case context_state_t::FLOW_MAPPING: // heap-allocated node will be freed in handling the corresponding SEQUENCE_FLOW_END event. m_context_stack.emplace_back( - line, indent, context_state_t::FLOW_SEQUENCE_KEY, new node_type(node_t::SEQUENCE)); + line, indent, context_state_t::FLOW_SEQUENCE_KEY, new basic_node_type(node_type::SEQUENCE)); mp_current_node = m_context_stack.back().p_node; break; default: { - *mp_current_node = node_type::sequence(); + *mp_current_node = basic_node_type::sequence(); parse_context& last_context = m_context_stack.back(); last_context.line = line; last_context.indent = indent; @@ -634,7 +651,7 @@ class basic_deserializer { m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::SEQUENCE_FLOW_END: { - if (m_flow_context_depth == 0) { + if FK_YAML_UNLIKELY (m_flow_context_depth == 0) { throw parse_error("Flow sequence ending is found outside the flow context.", line, indent); } --m_flow_context_depth; @@ -654,7 +671,7 @@ class basic_deserializer { }); bool is_valid = itr != m_context_stack.rend(); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw parse_error("No corresponding flow sequence beginning is found.", line, indent); } @@ -669,7 +686,7 @@ class basic_deserializer { // handle cases where the flow sequence is a mapping key node. if (!m_context_stack.empty() && state == context_state_t::FLOW_SEQUENCE_KEY) { - node_type key_node = std::move(*mp_current_node); + basic_node_type key_node = std::move(*mp_current_node); delete mp_current_node; mp_current_node = m_context_stack.back().p_node; m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; @@ -678,9 +695,9 @@ class basic_deserializer { break; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { - node_type key_node = node_type::mapping(); + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { + basic_node_type key_node = basic_node_type::mapping(); apply_directive_set(key_node); mp_current_node->swap(key_node); @@ -726,7 +743,7 @@ class basic_deserializer { } }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -740,7 +757,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("Flow mapping begininng is found without separated with a comma.", line, indent); } @@ -749,7 +766,7 @@ class basic_deserializer { switch (m_context_stack.back().state) { case context_state_t::BLOCK_SEQUENCE: case context_state_t::FLOW_SEQUENCE: - mp_current_node->template get_value_ref().emplace_back(node_type::mapping()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::mapping()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::FLOW_MAPPING, mp_current_node); break; @@ -757,11 +774,11 @@ class basic_deserializer { case context_state_t::FLOW_MAPPING: // heap-allocated node will be freed in handling the corresponding MAPPING_FLOW_END event. m_context_stack.emplace_back( - line, indent, context_state_t::FLOW_MAPPING_KEY, new node_type(node_t::MAPPING)); + line, indent, context_state_t::FLOW_MAPPING_KEY, new basic_node_type(node_type::MAPPING)); mp_current_node = m_context_stack.back().p_node; break; default: { - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); parse_context& last_context = m_context_stack.back(); last_context.line = line; last_context.indent = indent; @@ -779,7 +796,7 @@ class basic_deserializer { m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::MAPPING_FLOW_END: { - if (m_flow_context_depth == 0) { + if FK_YAML_UNLIKELY (m_flow_context_depth == 0) { throw parse_error("Flow mapping ending is found outside the flow context.", line, indent); } --m_flow_context_depth; @@ -799,7 +816,7 @@ class basic_deserializer { }); bool is_valid = itr != m_context_stack.rend(); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw parse_error("No corresponding flow mapping beginning is found.", line, indent); } @@ -814,7 +831,7 @@ class basic_deserializer { // handle cases where the flow mapping is a mapping key node. if (!m_context_stack.empty() && state == context_state_t::FLOW_MAPPING_KEY) { - node_type key_node = std::move(*mp_current_node); + basic_node_type key_node = std::move(*mp_current_node); delete mp_current_node; mp_current_node = m_context_stack.back().p_node; m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; @@ -823,9 +840,9 @@ class basic_deserializer { break; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { - node_type key_node = node_type::mapping(); + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { + basic_node_type key_node = basic_node_type::mapping(); apply_directive_set(key_node); mp_current_node->swap(key_node); @@ -849,18 +866,17 @@ class basic_deserializer { } case lexical_token_t::VALUE_SEPARATOR: FK_YAML_ASSERT(m_flow_context_depth > 0); - if (m_flow_token_state != flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("invalid value separator is found.", line, indent); } m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::ALIAS_PREFIX: - case lexical_token_t::NULL_VALUE: - case lexical_token_t::BOOLEAN_VALUE: - case lexical_token_t::INTEGER_VALUE: - case lexical_token_t::FLOAT_NUMBER_VALUE: - case lexical_token_t::STRING_VALUE: { - bool do_continue = deserialize_scalar(lexer, indent, line, type); + case lexical_token_t::PLAIN_SCALAR: + case lexical_token_t::SINGLE_QUOTED_SCALAR: + case lexical_token_t::DOUBLE_QUOTED_SCALAR: + case lexical_token_t::BLOCK_SCALAR: { + bool do_continue = deserialize_scalar(lexer, indent, line, token); if (do_continue) { continue; } @@ -870,16 +886,16 @@ class basic_deserializer { case lexical_token_t::END_OF_BUFFER: // This handles an empty input. case lexical_token_t::END_OF_DIRECTIVES: case lexical_token_t::END_OF_DOCUMENT: - last_type = type; + last_type = token.type; return; } - type = lexer.get_next_token(); + token = lexer.get_next_token(); indent = lexer.get_last_token_begin_pos(); line = lexer.get_lines_processed(); - } while (type != lexical_token_t::END_OF_BUFFER); + } while (token.type != lexical_token_t::END_OF_BUFFER); - last_type = type; + last_type = token.type; } /// @brief Deserializes YAML node properties (anchor and/or tag names) if they exist @@ -888,26 +904,26 @@ class basic_deserializer { /// @param line The variable to store the line of either the first property or the last non-property token. /// @param indent The variable to store the indent of either the first property or the last non-property token. /// @return true if any property is found, false otherwise. - bool deserialize_node_properties(lexer_type& lexer, lexical_token_t& last_type, uint32_t& line, uint32_t& indent) { + bool deserialize_node_properties(lexer_type& lexer, lexical_token& last_token, uint32_t& line, uint32_t& indent) { m_needs_anchor_impl = m_needs_tag_impl = false; - lexical_token_t type = last_type; + lexical_token token = last_token; bool ends_loop {false}; do { if (line < lexer.get_lines_processed()) { break; } - switch (type) { + switch (token.type) { case lexical_token_t::ANCHOR_PREFIX: - if (m_needs_anchor_impl) { + if FK_YAML_UNLIKELY (m_needs_anchor_impl) { throw parse_error( "anchor name cannot be specified more than once to the same node.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - m_anchor_name = lexer.get_string(); + m_anchor_name.assign(token.str.begin(), token.str.end()); m_needs_anchor_impl = true; if (!m_needs_tag_impl) { @@ -915,17 +931,17 @@ class basic_deserializer { indent = lexer.get_last_token_begin_pos(); } - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; case lexical_token_t::TAG_PREFIX: { - if (m_needs_tag_impl) { + if FK_YAML_UNLIKELY (m_needs_tag_impl) { throw parse_error( "tag name cannot be specified more than once to the same node.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - m_tag_name = lexer.get_string(); + m_tag_name.assign(token.str.begin(), token.str.end()); m_needs_tag_impl = true; if (!m_needs_anchor_impl) { @@ -933,7 +949,7 @@ class basic_deserializer { indent = lexer.get_last_token_begin_pos(); } - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; } default: @@ -942,7 +958,7 @@ class basic_deserializer { } } while (!ends_loop); - last_type = type; + last_token = token; bool prop_specified = m_needs_anchor_impl || m_needs_tag_impl; if (!prop_specified) { line = lexer.get_lines_processed(); @@ -956,7 +972,7 @@ class basic_deserializer { /// @param key a key string to be added to the current YAML node. /// @param line The line where the key is found. /// @param indent The indentation width in the current line where the key is found. - void add_new_key(node_type&& key, const uint32_t line, const uint32_t indent) { + void add_new_key(basic_node_type&& key, const uint32_t line, const uint32_t indent) { if (m_flow_context_depth == 0) { uint32_t pop_num = 0; if (indent == 0) { @@ -969,7 +985,7 @@ class basic_deserializer { return (indent == c.indent) && (c.state == context_state_t::BLOCK_MAPPING); }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -983,18 +999,18 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { throw parse_error("Flow mapping entry is found without separated with a comma.", line, indent); } if (mp_current_node->is_sequence()) { - mp_current_node->template get_value_ref().emplace_back(node_type::mapping()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::mapping()); mp_current_node = &(mp_current_node->operator[](mp_current_node->size() - 1)); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); } - auto itr = mp_current_node->template get_value_ref().emplace(std::move(key), node_type()); - if (!itr.second) { + auto itr = mp_current_node->template get_value_ref().emplace(std::move(key), basic_node_type()); + if FK_YAML_UNLIKELY (!itr.second) { throw parse_error("Detected duplication in mapping keys.", line, indent); } @@ -1005,11 +1021,11 @@ class basic_deserializer { } /// @brief Assign node value to the current node. - /// @param node_value A rvalue node_type object to be assigned to the current node. - void assign_node_value(node_type&& node_value, const uint32_t line, const uint32_t indent) { + /// @param node_value A rvalue basic_node_type object to be assigned to the current node. + void assign_node_value(basic_node_type&& node_value, const uint32_t line, const uint32_t indent) { if (mp_current_node->is_sequence()) { if (m_flow_context_depth > 0) { - if (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { + if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { throw parse_error("flow sequence entry is found without separated with a comma.", line, indent); } m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX; @@ -1037,14 +1053,20 @@ class basic_deserializer { /// @param indent The last indent size. /// @param line The last line. /// @return The created YAML scalar node. - node_type create_scalar_node(lexer_type& lexer, lexical_token_t type, uint32_t indent, uint32_t line) { + basic_node_type create_scalar_node(const lexical_token& token, uint32_t indent, uint32_t line) { + lexical_token_t type = token.type; FK_YAML_ASSERT( - type == lexical_token_t::NULL_VALUE || type == lexical_token_t::BOOLEAN_VALUE || - type == lexical_token_t::INTEGER_VALUE || type == lexical_token_t::FLOAT_NUMBER_VALUE || - type == lexical_token_t::STRING_VALUE || type == lexical_token_t::ALIAS_PREFIX); + type == lexical_token_t::PLAIN_SCALAR || type == lexical_token_t::SINGLE_QUOTED_SCALAR || + type == lexical_token_t::DOUBLE_QUOTED_SCALAR || type == lexical_token_t::BLOCK_SCALAR || + type == lexical_token_t::ALIAS_PREFIX); + + node_type value_type {node_type::STRING}; + if (type == lexical_token_t::PLAIN_SCALAR) { + value_type = scalar_scanner::scan(token.str.begin(), token.str.end()); + } if (m_needs_tag_impl) { - if (type == lexical_token_t::ALIAS_PREFIX) { + if FK_YAML_UNLIKELY (type == lexical_token_t::ALIAS_PREFIX) { throw parse_error("Tag cannot be specified to alias nodes", line, indent); } @@ -1054,24 +1076,24 @@ class basic_deserializer { switch (tag_type) { case tag_t::NULL_VALUE: - type = lexical_token_t::NULL_VALUE; + value_type = node_type::NULL_OBJECT; break; case tag_t::BOOLEAN: - type = lexical_token_t::BOOLEAN_VALUE; + value_type = node_type::BOOLEAN; break; case tag_t::INTEGER: - type = lexical_token_t::INTEGER_VALUE; + value_type = node_type::INTEGER; break; case tag_t::FLOATING_NUMBER: - type = lexical_token_t::FLOAT_NUMBER_VALUE; + value_type = node_type::FLOAT; break; case tag_t::STRING: - type = lexical_token_t::STRING_VALUE; + value_type = node_type::STRING; break; case tag_t::NON_SPECIFIC: // scalars with the non-specific tag is resolved to a string tag. // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags. - type = lexical_token_t::STRING_VALUE; + value_type = node_type::STRING; break; case tag_t::CUSTOM_TAG: default: @@ -1079,34 +1101,66 @@ class basic_deserializer { } } - node_type node {}; - switch (type) { - case lexical_token_t::NULL_VALUE: - node = node_type(lexer.get_null()); - break; - case lexical_token_t::BOOLEAN_VALUE: - node = node_type(lexer.get_boolean()); - break; - case lexical_token_t::INTEGER_VALUE: - node = node_type(lexer.get_integer()); + basic_node_type node {}; + + if (type == lexical_token_t::ALIAS_PREFIX) { + const std::string token_str = std::string(token.str.begin(), token.str.end()); + + uint32_t anchor_counts = static_cast(mp_meta->anchor_table.count(token_str)); + if FK_YAML_UNLIKELY (anchor_counts == 0) { + throw parse_error("The given anchor name must appear prior to the alias node.", line, indent); + } + + node.m_attrs |= detail::node_attr_bits::alias_bit; + node.m_prop.anchor = std::move(token_str); + detail::node_attr_bits::set_anchor_offset(anchor_counts - 1, node.m_attrs); + + apply_directive_set(node); + apply_node_properties(node); + + return node; + } + + switch (value_type) { + case node_type::NULL_OBJECT: { + std::nullptr_t null = nullptr; + bool converted = detail::aton(token.str.begin(), token.str.end(), null); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a null.", line, indent); + } + // The above `node` variable is already null, so no instance creation is needed. break; - case lexical_token_t::FLOAT_NUMBER_VALUE: - node = node_type(lexer.get_float_number()); + } + case node_type::BOOLEAN: { + boolean_type boolean = static_cast(false); + bool converted = detail::atob(token.str.begin(), token.str.end(), boolean); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a boolean.", line, indent); + } + node = basic_node_type(boolean); break; - case lexical_token_t::STRING_VALUE: - node = node_type(lexer.get_string()); + } + case node_type::INTEGER: { + integer_type integer = 0; + bool converted = detail::atoi(token.str.begin(), token.str.end(), integer); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to an integer.", line, indent); + } + node = basic_node_type(integer); break; - case lexical_token_t::ALIAS_PREFIX: { - const string_type& alias_name = lexer.get_string(); - uint32_t anchor_counts = static_cast(mp_meta->anchor_table.count(alias_name)); - if (anchor_counts == 0) { - throw parse_error("The given anchor name must appear prior to the alias node.", line, indent); + } + case node_type::FLOAT: { + float_number_type float_val = 0; + bool converted = detail::atof(token.str.begin(), token.str.end(), float_val); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a floating point value", line, indent); } - node.m_prop.anchor_status = detail::anchor_status_t::ALIAS; - node.m_prop.anchor = alias_name; - node.m_prop.anchor_offset = anchor_counts - 1; + node = basic_node_type(float_val); break; } + case node_type::STRING: + node = basic_node_type(std::string(token.str.begin(), token.str.end())); + break; default: // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } @@ -1123,16 +1177,16 @@ class basic_deserializer { /// @param indent The current indentation width. Can be updated in this function. /// @param line The number of processed lines. Can be updated in this function. /// @return true if next token has already been got, false otherwise. - bool deserialize_scalar(lexer_type& lexer, uint32_t& indent, uint32_t& line, lexical_token_t& type) { - node_type node = create_scalar_node(lexer, type, indent, line); + bool deserialize_scalar(lexer_type& lexer, uint32_t& indent, uint32_t& line, lexical_token& token) { + basic_node_type node = create_scalar_node(token, indent, line); if (mp_current_node->is_mapping()) { add_new_key(std::move(node), line, indent); return false; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { if (line != lexer.get_lines_processed()) { // This path is for explicit mapping key separator like: // @@ -1159,7 +1213,7 @@ class basic_deserializer { m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); break; default: - if (cur_context.line == line) { + if FK_YAML_UNLIKELY (cur_context.line == line) { throw parse_error("Multiple mapping keys are specified on the same line.", line, indent); } cur_context.line = line; @@ -1168,7 +1222,7 @@ class basic_deserializer { break; } - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); } add_new_key(std::move(node), line, indent); @@ -1182,14 +1236,14 @@ class basic_deserializer { } /// @brief Set YAML directive properties to the given node. - /// @param node A node_type object to be set YAML directive properties. - void apply_directive_set(node_type& node) noexcept { + /// @param node A basic_node_type object to be set YAML directive properties. + void apply_directive_set(basic_node_type& node) noexcept { node.mp_meta = mp_meta; } /// @brief Set YAML node properties (anchor and/or tag names) to the given node. /// @param node A node type object to be set YAML node properties. - void apply_node_properties(node_type& node) { + void apply_node_properties(basic_node_type& node) { if (m_needs_anchor_impl) { node.add_anchor_name(m_anchor_name); m_needs_anchor_impl = false; @@ -1205,13 +1259,13 @@ class basic_deserializer { /// @brief Update the target YAML version with an input string. /// @param version_str A YAML version string. - yaml_version_t convert_yaml_version(const string_type& version_str) noexcept { - return (version_str == "1.1") ? yaml_version_t::VER_1_1 : yaml_version_t::VER_1_2; + yaml_version_type convert_yaml_version(str_view version_str) noexcept { + return (version_str.compare("1.1") == 0) ? yaml_version_type::VERSION_1_1 : yaml_version_type::VERSION_1_2; } private: /// The currently focused YAML node. - node_type* mp_current_node {nullptr}; + basic_node_type* mp_current_node {nullptr}; /// The stack of parse contexts. std::deque m_context_stack {}; /// The current depth of flow contexts. @@ -1225,9 +1279,9 @@ class basic_deserializer { /// A flag to determine the need for a value separator or a flow suffix to follow. flow_token_state_t m_flow_token_state {flow_token_state_t::NEEDS_VALUE_OR_SUFFIX}; /// The last YAML anchor name. - string_type m_anchor_name {}; + std::string m_anchor_name {}; /// The last tag name. - string_type m_tag_name {}; + std::string m_tag_name {}; }; FK_YAML_DETAIL_NAMESPACE_END diff --git a/include/fkYAML/detail/input/input_adapter.hpp b/include/fkYAML/detail/input/input_adapter.hpp index 67c91082..a472ca37 100644 --- a/include/fkYAML/detail/input/input_adapter.hpp +++ b/include/fkYAML/detail/input/input_adapter.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -20,10 +20,12 @@ #include #include -#include +#include #include #include +#include #include +#include #include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -38,9 +40,7 @@ class iterator_input_adapter; /// @brief An input adapter for iterators of type char. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -49,10 +49,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { } // allow only move construct/assignment like other input adapters. @@ -62,34 +64,32 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { - buffer.clear(); - buffer.reserve(std::distance(m_current, m_end)); + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { + m_buffer.clear(); switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of fill_buffer() for UTF-8 encoded inputs. - /// @param buffer A buffer to be filled with the input. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); - IterType current = m_current; + IterType current = m_begin; while (current != m_end) { uint8_t first = uint8_t(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -98,7 +98,7 @@ class iterator_input_adapter< case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -106,7 +106,7 @@ class iterator_input_adapter< case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -115,7 +115,7 @@ class iterator_input_adapter< std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -126,18 +126,36 @@ class iterator_input_adapter< } } + IterType cr_or_end_itr = std::find(m_begin, m_end, '\r'); + if (cr_or_end_itr == m_end && m_is_contiguous) { + // The input iterators (begin, end) can be used as-is during parsing. + return str_view {m_begin, m_end}; + } + + m_buffer.reserve(std::distance(m_begin, m_end)); + + current = m_begin; do { - IterType cr_or_end_itr = std::find(m_current, m_end, '\r'); - buffer.append(m_current, cr_or_end_itr); - m_current = (cr_or_end_itr == m_end) ? cr_or_end_itr : std::next(cr_or_end_itr); - } while (m_current != m_end); + m_buffer.append(current, cr_or_end_itr); + if (cr_or_end_itr == m_end) { + break; + } + current = std::next(cr_or_end_itr); + cr_or_end_itr = std::find(current, m_end, '\r'); + } while (current != m_end); + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @param buffer A buffer to be filled with the input. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end) / 2); + int shift_bits[2] {0, 0}; if (m_encode_type == utf_encode_t::UTF_16BE) { shift_bits[0] = 8; @@ -152,11 +170,14 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - while (m_current != m_end || encoded_buf_size != 0) { - while (m_current != m_end && encoded_buf_size < 2) { - char16_t utf16 = static_cast(uint8_t(*m_current++) << shift_bits[0]); - utf16 |= static_cast(uint8_t(*m_current++) << shift_bits[1]); - if (utf16 != char16_t(0x000Du)) { + IterType current = m_begin; + while (current != m_end || encoded_buf_size != 0) { + while (current != m_end && encoded_buf_size < 2) { + char16_t utf16 = static_cast(uint8_t(*current++) << shift_bits[0]); + utf16 |= static_cast(uint8_t(*current++) << shift_bits[1]); + + // skip appending CRs. + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -164,20 +185,26 @@ class iterator_input_adapter< uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end) / 4); + int shift_bits[4] {0, 0, 0, 0}; if (m_encode_type == utf_encode_t::UTF_32BE) { shift_bits[0] = 24; @@ -194,36 +221,41 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - while (m_current != m_end) { - char32_t utf32 = static_cast(*m_current++ << shift_bits[0]); - utf32 |= static_cast(*m_current++ << shift_bits[1]); - utf32 |= static_cast(*m_current++ << shift_bits[2]); - utf32 |= static_cast(*m_current++ << shift_bits[3]); + IterType current = m_begin; + while (current != m_end) { + char32_t utf32 = static_cast(*current++ << shift_bits[0]); + utf32 |= static_cast(*current++ << shift_bits[1]); + utf32 |= static_cast(*current++ << shift_bits[2]); + utf32 |= static_cast(*current++ << shift_bits[3]); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; -#ifdef FK_YAML_HAS_CHAR8_T +#if FK_YAML_HAS_CHAR8_T /// @brief An input adapter for iterators of type char8_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char8_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -232,10 +264,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { // char8_t characters must be encoded in the UTF-8 format. // See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0482r6.html. FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); @@ -248,10 +282,10 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { - IterType current = m_current; + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { + IterType current = m_begin; while (current != m_end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -260,7 +294,7 @@ class iterator_input_adapter< case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -268,7 +302,7 @@ class iterator_input_adapter< case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -277,7 +311,7 @@ class iterator_input_adapter< std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -288,32 +322,38 @@ class iterator_input_adapter< } } - buffer.reserve(std::distance(m_current, m_end)); - while (m_current != m_end) { - char c = char(*m_current++); - if (c != '\r') { - buffer.push_back(c); + m_buffer.reserve(std::distance(m_begin, m_end)); + current = m_begin; + + while (current != m_end) { + char c = char(*current++); + if FK_YAML_LIKELY (c != '\r') { + m_buffer.push_back(c); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; -#endif // defined(FK_YAML_HAS_CHAR8_T) +#endif // FK_YAML_HAS_CHAR8_T /// @brief An input adapter for iterators of type char16_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char16_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -322,10 +362,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); } @@ -336,9 +378,9 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { int shift_bits = (m_encode_type == utf_encode_t::UTF_16BE) ? 0 : 8; std::array encoded_buffer {{0, 0}}; @@ -346,16 +388,19 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - buffer.reserve(std::distance(m_current, m_end) * 2); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end)); - while (m_current != m_end || encoded_buf_size != 0) { - while (m_current != m_end && encoded_buf_size < 2) { - char16_t utf16 = *m_current++; + IterType current = m_begin; + while (current != m_end || encoded_buf_size != 0) { + while (current != m_end && encoded_buf_size < 2) { + char16_t utf16 = *current++; utf16 = char16_t( static_cast((utf16 & 0x00FFu) << shift_bits) | static_cast((utf16 & 0xFF00u) >> shift_bits)); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -363,31 +408,35 @@ class iterator_input_adapter< uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; encoded_buffer[1] = 0; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_16BE}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; /// @brief An input adapter for iterators of type char32_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char32_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -396,10 +445,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); } @@ -410,9 +461,9 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { int shift_bits[4] {0, 0, 0, 0}; if (m_encode_type == utf_encode_t::UTF_32LE) { shift_bits[0] = 24; @@ -424,30 +475,39 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - buffer.reserve(std::distance(m_current, m_end) * 4); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end)); - while (m_current != m_end) { - char32_t tmp = *m_current++; + IterType current = m_begin; + while (current != m_end) { + char32_t tmp = *current++; char32_t utf32 = char32_t( static_cast((tmp & 0xFF000000u) >> shift_bits[0]) | static_cast((tmp & 0x00FF0000u) >> shift_bits[1]) | static_cast((tmp & 0x0000FF00u) << shift_bits[2]) | static_cast((tmp & 0x000000FFu) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_UNLIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_32BE}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; /// @brief An input adapter for C-style file handles. @@ -474,28 +534,27 @@ class file_input_adapter { file_input_adapter& operator=(file_input_adapter&&) = default; ~file_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of get_character() for UTF-8 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); char tmp_buf[256] {}; @@ -514,13 +573,13 @@ class file_input_adapter { ++p_cr_or_end; } - buffer.append(p_current, p_cr_or_end); + m_buffer.append(p_current, p_cr_or_end); p_current = (p_cr_or_end == p_end) ? p_end : p_cr_or_end + 1; } while (p_current != p_end); } - auto current = buffer.begin(); - auto end = buffer.end(); + auto current = m_buffer.begin(); + auto end = m_buffer.end(); while (current != end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -529,7 +588,7 @@ class file_input_adapter { case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -537,7 +596,7 @@ class file_input_adapter { case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -546,7 +605,7 @@ class file_input_adapter { std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -556,11 +615,13 @@ class file_input_adapter { break; } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); int shift_bits[2] {0, 0}; @@ -582,7 +643,7 @@ class file_input_adapter { char16_t utf16 = char16_t( static_cast(uint8_t(chars[0]) << shift_bits[0]) | static_cast(uint8_t(chars[1]) << shift_bits[1])); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -590,18 +651,20 @@ class file_input_adapter { uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); int shift_bits[4] {0, 0, 0, 0}; @@ -623,7 +686,7 @@ class file_input_adapter { while (std::feof(m_file) == 0) { std::size_t size = std::fread(&chars[0], sizeof(char), 4, m_file); if (size != 4) { - return; + break; } char32_t utf32 = char32_t( @@ -632,11 +695,13 @@ class file_input_adapter { static_cast(uint8_t(chars[2]) << shift_bits[2]) | static_cast(uint8_t(chars[3]) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: @@ -644,6 +709,8 @@ class file_input_adapter { std::FILE* m_file {nullptr}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; }; /// @brief An input adapter for streams @@ -654,6 +721,7 @@ class stream_input_adapter { /// @brief Construct a new stream_input_adapter object. /// @param is A reference to the target input stream. + /// @param encode_type The encoding type for this input adapter. explicit stream_input_adapter(std::istream& is, utf_encode_t encode_type) noexcept : m_istream(&is), m_encode_type(encode_type) { @@ -666,28 +734,27 @@ class stream_input_adapter { stream_input_adapter& operator=(stream_input_adapter&&) = default; ~stream_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of get_character() for UTF-8 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); char tmp_buf[256] {}; @@ -707,13 +774,13 @@ class stream_input_adapter { ++p_cr_or_end; } - buffer.append(p_current, p_cr_or_end); + m_buffer.append(p_current, p_cr_or_end); p_current = (p_cr_or_end == p_end) ? p_end : p_cr_or_end + 1; } while (p_current != p_end); } while (!m_istream->eof()); - auto current = buffer.begin(); - auto end = buffer.end(); + auto current = m_buffer.begin(); + auto end = m_buffer.end(); while (current != end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -722,7 +789,7 @@ class stream_input_adapter { case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -730,7 +797,7 @@ class stream_input_adapter { case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -739,7 +806,7 @@ class stream_input_adapter { std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -749,11 +816,13 @@ class stream_input_adapter { break; } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); int shift_bits[2] {0, 0}; @@ -782,7 +851,7 @@ class stream_input_adapter { static_cast(uint8_t(chars[0]) << shift_bits[0]) | static_cast(uint8_t(chars[1]) << shift_bits[1])); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } }; @@ -790,18 +859,20 @@ class stream_input_adapter { uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } while (!m_istream->eof()); + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); int shift_bits[4] {0, 0, 0, 0}; @@ -824,7 +895,7 @@ class stream_input_adapter { m_istream->read(&chars[0], 4); std::streamsize size = m_istream->gcount(); if (size != 4) { - return; + break; } char32_t utf32 = char32_t( @@ -833,11 +904,13 @@ class stream_input_adapter { static_cast(uint8_t(chars[2]) << shift_bits[2]) | static_cast(uint8_t(chars[3]) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } while (!m_istream->eof()); + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: @@ -845,21 +918,47 @@ class stream_input_adapter { std::istream* m_istream {nullptr}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; }; ///////////////////////////////// // input_adapter providers // ///////////////////////////////// +namespace { + +template +inline iterator_input_adapter create_iterator_input_adapter( + ItrType begin, ItrType end, bool is_contiguous) noexcept { + utf_encode_t encode_type = utf_encode_detector::detect(begin, end); + return iterator_input_adapter(begin, end, encode_type, is_contiguous); +} + +} // anonymous namespace + /// @brief A factory method for iterator_input_adapter objects with ieterator values. /// @tparam ItrType An iterator type. /// @param begin The beginning of iterators. /// @param end The end of iterators. /// @return iterator_input_adapter An iterator_input_adapter object for the target iterator type. -template ())))> +template inline iterator_input_adapter input_adapter(ItrType begin, ItrType end) { - utf_encode_t encode_type = detect_encoding_and_skip_bom(begin, end); - return iterator_input_adapter(begin, end, encode_type); + constexpr bool is_random_access_itr = + std::is_same::iterator_category, std::random_access_iterator_tag>::value; + + // Check if `begin` & `end` are contiguous iterators. + // Getting distance between begin and (end - 1) avoids dereferencing an invalid sentinel. + bool is_contiguous = false; + if (is_random_access_itr) { + ptrdiff_t size = static_cast(std::distance(begin, end - 1)); + + using CharPtr = remove_cvref_t::pointer>; + CharPtr p_begin = &*begin; + CharPtr p_second_last = &*(end - 1); + is_contiguous = (p_second_last - p_begin == size); + } + return create_iterator_input_adapter(begin, end, is_contiguous); } /// @brief A factory method for iterator_input_adapter objects with C-style arrays. @@ -867,8 +966,8 @@ inline iterator_input_adapter input_adapter(ItrType begin, ItrType end) /// @tparam N A size of an array. /// @return decltype(input_adapter(array, array + N)) An iterator_input_adapter object for the target array. template -inline auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + (N - 1))) { - return input_adapter(array, array + (N - 1)); +inline auto input_adapter(T (&array)[N]) -> decltype(create_iterator_input_adapter(array, array + (N - 1), true)) { + return create_iterator_input_adapter(array, array + (N - 1), true); } /// @brief A namespace to implement container_input_adapter_factory for internal use. @@ -888,15 +987,18 @@ struct container_input_adapter_factory {}; template struct container_input_adapter_factory< ContainerType, void_t()), end(std::declval()))>> { + /// Whether or not ContainerType is a contiguous container. + static constexpr bool is_contiguous = is_contiguous_container::value; + /// A type for resulting input adapter object. - using adapter_type = - decltype(input_adapter(begin(std::declval()), end(std::declval()))); + using adapter_type = decltype(create_iterator_input_adapter( + begin(std::declval()), end(std::declval()), is_contiguous)); /// @brief A factory method of input adapter objects for the target container objects. /// @param container A container-like input object. /// @return adapter_type An iterator_input_adapter object. static adapter_type create(const ContainerType& container) { - return input_adapter(begin(container), end(container)); + return create_iterator_input_adapter(begin(container), end(container), is_contiguous); } }; @@ -916,10 +1018,10 @@ inline typename input_adapter_factory::container_input_adapter_factory @@ -11,37 +11,31 @@ #ifndef FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ #define FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ +#include #include -#include -#include #include -#include -#include -#include #include #include -#include #include #include #include -#include #include -#include -#include #include +#include #include #include FK_YAML_DETAIL_NAMESPACE_BEGIN +struct lexical_token { + lexical_token_t type {lexical_token_t::END_OF_BUFFER}; + str_view str {}; +}; + /// @brief A class which lexically analizes YAML formatted inputs. -/// @tparam BasicNodeType A type of the container for YAML values. -template ::value, int> = 0> class lexical_analyzer { private: - using char_traits_type = typename std::char_traits; - enum class block_style_indicator_t { LITERAL, //!< keeps newlines inside the block as they are indicated by a pipe `|`. FOLDED, //!< replaces newlines inside the block with spaces indicated by a right angle bracket `>`. @@ -54,25 +48,19 @@ class lexical_analyzer { }; public: - using boolean_type = typename BasicNodeType::boolean_type; - using integer_type = typename BasicNodeType::integer_type; - using float_number_type = typename BasicNodeType::float_number_type; - using string_type = typename BasicNodeType::string_type; - /// @brief Construct a new lexical_analyzer object. /// @tparam InputAdapterType The type of the input adapter. /// @param input_adapter An input adapter object. - template ::value, int> = 0> - explicit lexical_analyzer(InputAdapterType&& input_adapter) { - std::forward(input_adapter).fill_buffer(m_input_buffer); - m_cur_itr = m_token_begin_itr = m_input_buffer.cbegin(); - m_end_itr = m_input_buffer.cend(); + explicit lexical_analyzer(str_view input_buffer) noexcept + : m_input_buffer(input_buffer), + m_cur_itr(m_input_buffer.begin()), + m_end_itr(m_input_buffer.end()) { m_pos_tracker.set_target_buffer(m_input_buffer); } - /// @brief Get the next lexical token type by scanning the left of the input buffer. - /// @return lexical_token_t The next lexical token type. - lexical_token_t get_next_token() { + /// @brief Get the next lexical token by scanning the left of the input buffer. + /// @return lexical_token The next lexical token. + lexical_token get_next_token() { skip_white_spaces_and_newline_codes(); m_token_begin_itr = m_cur_itr; @@ -81,25 +69,31 @@ class lexical_analyzer { m_last_token_begin_line = m_pos_tracker.get_lines_read(); if (m_cur_itr == m_end_itr) { - return lexical_token_t::END_OF_BUFFER; + return {}; } - switch (char current = *m_cur_itr) { + lexical_token token {}; + token.type = lexical_token_t::PLAIN_SCALAR; + + switch (*m_cur_itr) { case '?': if (++m_cur_itr == m_end_itr) { - m_value_buffer = "?"; - return lexical_token_t::STRING_VALUE; + token.str = str_view {m_token_begin_itr, m_end_itr}; + return token; } switch (*m_cur_itr) { case ' ': - return lexical_token_t::EXPLICIT_KEY_PREFIX; + token.type = lexical_token_t::EXPLICIT_KEY_PREFIX; + return token; default: - return scan_scalar(); + scan_scalar(token); + return token; } case ':': { // key separater if (++m_cur_itr == m_end_itr) { - return lexical_token_t::KEY_SEPARATOR; + token.type = lexical_token_t::KEY_SEPARATOR; + return token; } switch (*m_cur_itr) { @@ -117,41 +111,50 @@ class lexical_analyzer { // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. break; } - return scan_scalar(); + scan_scalar(token); + return token; default: - return scan_scalar(); + scan_scalar(token); + return token; } - return lexical_token_t::KEY_SEPARATOR; + token.type = lexical_token_t::KEY_SEPARATOR; + return token; } case ',': // value separater ++m_cur_itr; - return lexical_token_t::VALUE_SEPARATOR; + token.type = lexical_token_t::VALUE_SEPARATOR; + return token; case '&': { // anchor prefix - extract_anchor_name(); - bool is_empty = m_value_buffer.empty(); - if (is_empty) { + extract_anchor_name(token); + bool is_empty = token.str.empty(); + if FK_YAML_UNLIKELY (is_empty) { emit_error("anchor name must not be empty."); } - return lexical_token_t::ANCHOR_PREFIX; + + token.type = lexical_token_t::ANCHOR_PREFIX; + return token; } case '*': { // alias prefix - extract_anchor_name(); - bool is_empty = m_value_buffer.empty(); - if (is_empty) { + extract_anchor_name(token); + bool is_empty = token.str.empty(); + if FK_YAML_UNLIKELY (is_empty) { emit_error("anchor name must not be empty."); } - return lexical_token_t::ALIAS_PREFIX; + token.type = lexical_token_t::ALIAS_PREFIX; + return token; } case '!': - extract_tag_name(); - return lexical_token_t::TAG_PREFIX; + extract_tag_name(token); + token.type = lexical_token_t::TAG_PREFIX; + return token; case '#': // comment prefix scan_comment(); return get_next_token(); case '%': // directive prefix - return scan_directive(); + token.type = scan_directive(); + return token; case '-': { char next = *(m_cur_itr + 1); switch (next) { @@ -160,71 +163,93 @@ class lexical_analyzer { case '\n': // Move a cursor to the beginning of the next token. m_cur_itr += 2; - return lexical_token_t::SEQUENCE_BLOCK_PREFIX; + token.type = lexical_token_t::SEQUENCE_BLOCK_PREFIX; + return token; default: break; } - bool is_available = (std::distance(m_cur_itr, m_end_itr) > 2); + bool is_available = ((m_end_itr - m_cur_itr) > 2); if (is_available) { - if (std::equal(m_token_begin_itr, m_cur_itr + 3, "---")) { + bool is_dir_end = std::equal(m_token_begin_itr, m_cur_itr + 3, "---"); + if (is_dir_end) { m_cur_itr += 3; - return lexical_token_t::END_OF_DIRECTIVES; + token.type = lexical_token_t::END_OF_DIRECTIVES; + return token; } } - return scan_scalar(); + scan_scalar(token); + return token; } case '[': // sequence flow begin m_flow_context_depth++; ++m_cur_itr; - return lexical_token_t::SEQUENCE_FLOW_BEGIN; + token.type = lexical_token_t::SEQUENCE_FLOW_BEGIN; + return token; case ']': // sequence flow end - m_flow_context_depth--; + m_flow_context_depth = (m_flow_context_depth > 0) ? m_flow_context_depth - 1 : 0; ++m_cur_itr; - return lexical_token_t::SEQUENCE_FLOW_END; + token.type = lexical_token_t::SEQUENCE_FLOW_END; + return token; case '{': // mapping flow begin m_flow_context_depth++; ++m_cur_itr; - return lexical_token_t::MAPPING_FLOW_BEGIN; + token.type = lexical_token_t::MAPPING_FLOW_BEGIN; + return token; case '}': // mapping flow end - m_flow_context_depth--; + m_flow_context_depth = (m_flow_context_depth > 0) ? m_flow_context_depth - 1 : 0; ++m_cur_itr; - return lexical_token_t::MAPPING_FLOW_END; + token.type = lexical_token_t::MAPPING_FLOW_END; + return token; case '@': emit_error("Any token cannot start with at(@). It is a reserved indicator for YAML."); case '`': emit_error("Any token cannot start with grave accent(`). It is a reserved indicator for YAML."); case '\"': case '\'': - return scan_scalar(); + scan_scalar(token); + return token; case '+': - return scan_scalar(); + scan_scalar(token); + return token; case '.': { - bool is_available = (std::distance(m_cur_itr, m_end_itr) > 2); + bool is_available = ((m_end_itr - m_cur_itr) > 2); if (is_available) { - if (std::equal(m_cur_itr, m_cur_itr + 3, "...")) { + bool is_doc_end = std::equal(m_cur_itr, m_cur_itr + 3, "..."); + if (is_doc_end) { m_cur_itr += 3; - return lexical_token_t::END_OF_DOCUMENT; + token.type = lexical_token_t::END_OF_DOCUMENT; + return token; } } - return scan_scalar(); + scan_scalar(token); + return token; } case '|': { chomping_indicator_t chomp_type = chomping_indicator_t::KEEP; uint32_t indent = 0; + ++m_cur_itr; get_block_style_metadata(chomp_type, indent); - return scan_block_style_string_token(block_style_indicator_t::LITERAL, chomp_type, indent); + scan_block_style_string_token(block_style_indicator_t::LITERAL, chomp_type, indent); + token.type = lexical_token_t::BLOCK_SCALAR; + token.str = m_value_buffer; + return token; } case '>': { chomping_indicator_t chomp_type = chomping_indicator_t::KEEP; uint32_t indent = 0; + ++m_cur_itr; get_block_style_metadata(chomp_type, indent); - return scan_block_style_string_token(block_style_indicator_t::FOLDED, chomp_type, indent); + scan_block_style_string_token(block_style_indicator_t::FOLDED, chomp_type, indent); + token.type = lexical_token_t::BLOCK_SCALAR; + token.str = m_value_buffer; + return token; } default: - return scan_scalar(); + scan_scalar(token); + return token; } } @@ -240,65 +265,21 @@ class lexical_analyzer { return m_last_token_begin_line; } - /// @brief Convert from string to null and get the converted value. - /// @return std::nullptr_t A null value converted from one of the followings: "null", "Null", "NULL", "~". - std::nullptr_t get_null() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to boolean and get the converted value. - /// @retval true A string token is one of the followings: "true", "True", "TRUE". - /// @retval false A string token is one of the followings: "false", "False", "FALSE". - boolean_type get_boolean() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to integer and get the converted value. - /// @return integer_type An integer value converted from the source string. - integer_type get_integer() const { - if (m_value_buffer.size() > 2 && m_value_buffer.rfind("0o", 0) != std::string::npos) { - // Replace the prefix "0o" with "0" since STL functions can detect octal chars. - // Note that the YAML specifies octal values start with the prefix "0o", not "0". - // See https://yaml.org/spec/1.2.2/#1032-tag-resolution for more details. - return from_string("0" + m_value_buffer.substr(2), type_tag {}); - } - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to float number and get the converted value. - /// @return float_number_type A float number value converted from the source string. - float_number_type get_float_number() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Get a scanned string value. - /// @return const string_type& Constant reference to a scanned string. - const string_type& get_string() const noexcept { - // TODO: Provide support for different string types between nodes & inputs. - static_assert(std::is_same::value, "Unsupported, different string types."); - return m_value_buffer; - } - /// @brief Get the YAML version specification. - /// @return const string_type& A YAML version specification. - const string_type& get_yaml_version() const { - FK_YAML_ASSERT(!m_value_buffer.empty() && m_value_buffer.size() == 3); - FK_YAML_ASSERT(m_value_buffer == "1.1" || m_value_buffer == "1.2"); - - return m_value_buffer; + /// @return str_view A YAML version specification. + str_view get_yaml_version() const noexcept { + return m_yaml_version; } /// @brief Get the YAML tag handle defined in the TAG directive. - /// @return const std::string& A tag handle. - const std::string& get_tag_handle() const { - FK_YAML_ASSERT(!m_tag_handle.empty()); + /// @return str_view A tag handle. + str_view get_tag_handle() const noexcept { return m_tag_handle; } /// @brief Get the YAML tag prefix defined in the TAG directive. - /// @return const std::string A tag prefix. - const std::string& get_tag_prefix() const { - FK_YAML_ASSERT(!m_tag_prefix.empty()); + /// @return str_view A tag prefix. + str_view get_tag_prefix() const noexcept { return m_tag_prefix; } @@ -334,19 +315,19 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + str_view dir_name(m_token_begin_itr, m_cur_itr); - if (m_value_buffer == "TAG") { - if (!ends_loop) { + if (dir_name == "TAG") { + if FK_YAML_UNLIKELY (!ends_loop) { emit_error("There must be at least one white space between \"%TAG\" and tag info."); } skip_white_spaces(); return scan_tag_directive(); } - if (m_value_buffer == "YAML") { - if (!ends_loop) { - emit_error("There must be at least one white space between \"%YAML\" and tag info."); + if (dir_name == "YAML") { + if FK_YAML_UNLIKELY (!ends_loop) { + emit_error("There must be at least one white space between \"%YAML\" and version."); } skip_white_spaces(); return scan_yaml_version_directive(); @@ -359,19 +340,17 @@ class lexical_analyzer { /// @brief Scan a YAML tag directive. /// @return lexical_token_t The lexical token type for YAML tag directives. lexical_token_t scan_tag_directive() { - m_tag_handle.clear(); - m_tag_prefix.clear(); m_token_begin_itr = m_cur_itr; // // extract a tag handle // - if (*m_cur_itr != '!') { + if FK_YAML_UNLIKELY (*m_cur_itr != '!') { emit_error("Tag handle must start with \'!\'."); } - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } @@ -381,10 +360,10 @@ class lexical_analyzer { // primary handle (!) break; case '!': - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } - if (*m_cur_itr != ' ' && *m_cur_itr != '\t') { + if FK_YAML_UNLIKELY (*m_cur_itr != ' ' && *m_cur_itr != '\t') { emit_error("invalid tag handle is found."); } break; @@ -401,7 +380,7 @@ class lexical_analyzer { break; } char next = *(m_cur_itr + 1); - if (next != ' ' && next != '\t') { + if FK_YAML_UNLIKELY (next != ' ' && next != '\t') { emit_error("invalid tag handle is found."); } ends_loop = true; @@ -410,14 +389,14 @@ class lexical_analyzer { case '-': break; default: - if (!isalnum(*m_cur_itr)) { + if FK_YAML_UNLIKELY (!isalnum(*m_cur_itr)) { // See https://yaml.org/spec/1.2.2/#rule-c-named-tag-handle for more details. emit_error("named handle can contain only numbers(0-9), alphabets(A-Z,a-z) and hyphens(-)."); } break; } - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } } while (!ends_loop); @@ -425,7 +404,7 @@ class lexical_analyzer { } } - m_tag_handle.assign(m_token_begin_itr, m_cur_itr); + m_tag_handle = str_view {m_token_begin_itr, m_cur_itr}; skip_white_spaces(); @@ -434,6 +413,7 @@ class lexical_analyzer { // m_token_begin_itr = m_cur_itr; + const char* p_tag_prefix_begin = m_cur_itr; switch (*m_cur_itr) { // a tag prefix must not start with flow indicators to avoid ambiguity. // See https://yaml.org/spec/1.2.2/#rule-ns-global-tag-prefix for more details. @@ -457,13 +437,13 @@ class lexical_analyzer { } } while (!ends_loop && ++m_cur_itr != m_end_itr); - m_tag_prefix.assign(m_token_begin_itr, m_cur_itr); - - bool is_valid = uri_encoding::validate(m_tag_prefix.begin(), m_tag_prefix.end()); - if (!is_valid) { + bool is_valid = uri_encoding::validate(p_tag_prefix_begin, m_cur_itr); + if FK_YAML_UNLIKELY (!is_valid) { emit_error("invalid URI character is found in a tag prefix."); } + m_tag_prefix = str_view {p_tag_prefix_begin, m_cur_itr}; + return lexical_token_t::TAG_DIRECTIVE; } @@ -471,7 +451,6 @@ class lexical_analyzer { /// @note Only 1.1 and 1.2 are supported. If not, throws an exception. /// @return lexical_token_t The lexical token type for YAML version directives. lexical_token_t scan_yaml_version_directive() { - m_value_buffer.clear(); m_token_begin_itr = m_cur_itr; bool ends_loop = false; @@ -488,20 +467,20 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + m_yaml_version = str_view {m_token_begin_itr, m_cur_itr}; - if (m_value_buffer != "1.1" && m_value_buffer != "1.2") { + if FK_YAML_UNLIKELY (m_yaml_version.compare("1.1") != 0 && m_yaml_version.compare("1.2") != 0) { emit_error("Only 1.1 and 1.2 can be specified as the YAML version."); } return lexical_token_t::YAML_VER_DIRECTIVE; } - /// @brief Extracts an anchor name from the input and assigns the result to `m_value_buffer`. - void extract_anchor_name() { + /// @brief Extracts an anchor name from the input. + /// @param token The token into which the extraction result is written. + void extract_anchor_name(lexical_token& token) { FK_YAML_ASSERT(*m_cur_itr == '&' || *m_cur_itr == '*'); - m_value_buffer.clear(); m_token_begin_itr = ++m_cur_itr; bool ends_loop = false; @@ -528,18 +507,17 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + token.str = str_view {m_token_begin_itr, m_cur_itr}; } - /// @brief Extracts a tag name from the input and assigns the result to `m_value_buffer`. - void extract_tag_name() { - m_value_buffer.clear(); - + /// @brief Extracts a tag name from the input. + /// @param token The token into which the extraction result is written. + void extract_tag_name(lexical_token& token) { FK_YAML_ASSERT(*m_cur_itr == '!'); if (++m_cur_itr == m_end_itr) { // Just "!" is a non-specific tag. - m_value_buffer = "!"; + token.str = str_view {m_token_begin_itr, m_end_itr}; return; } @@ -550,7 +528,7 @@ class lexical_analyzer { case ' ': case '\n': // Just "!" is a non-specific tag. - m_value_buffer = "!"; + token.str = str_view {m_token_begin_itr, m_cur_itr}; return; case '!': // Secondary tag handles (!!suffix) @@ -581,7 +559,7 @@ class lexical_analyzer { ends_loop = true; break; case '!': - if (!allows_another_tag_prefix) { + if FK_YAML_UNLIKELY (!allows_another_tag_prefix) { emit_error("invalid tag prefix (!) is found."); } @@ -594,22 +572,22 @@ class lexical_analyzer { } } while (!ends_loop); - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + token.str = str_view {m_token_begin_itr, m_cur_itr}; if (is_verbatim) { - char last = m_value_buffer.back(); - if (last != '>') { + char last = token.str.back(); + if FK_YAML_UNLIKELY (last != '>') { emit_error("verbatim tag (!) must be ended with \'>\'."); } - auto tag_begin = m_value_buffer.begin() + 2; - auto tag_end = m_value_buffer.end() - 1; - if (tag_begin == tag_end) { + // only the `TAG` part of the `!` for URI validation. + str_view tag_body = token.str.substr(2, token.str.size() - 3); + if FK_YAML_UNLIKELY (tag_body.empty()) { emit_error("verbatim tag(!) must not be empty."); } - bool is_valid_uri = uri_encoding::validate(tag_begin, tag_end); - if (!is_valid_uri) { + bool is_valid_uri = uri_encoding::validate(tag_body.begin(), tag_body.end()); + if FK_YAML_UNLIKELY (!is_valid_uri) { emit_error("invalid URI character is found in a verbatim tag."); } @@ -617,27 +595,29 @@ class lexical_analyzer { } if (is_named_handle) { - char last = m_value_buffer.back(); - if (last == '!') { + char last = token.str.back(); + if FK_YAML_UNLIKELY (last == '!') { // Tag shorthand must be followed by a non-empty suffix. // See the "Tag Shorthands" section in https://yaml.org/spec/1.2.2/#691-node-tags. emit_error("named handle has no suffix."); } - std::size_t last_tag_prefix_pos = m_value_buffer.find_last_of('!'); - FK_YAML_ASSERT(last_tag_prefix_pos != std::string::npos); + // get the position of the beginning of a suffix. (!handle!suffix) + std::size_t last_tag_prefix_pos = token.str.find_last_of('!'); + FK_YAML_ASSERT(last_tag_prefix_pos != str_view::npos); - bool is_valid_uri = - uri_encoding::validate(m_value_buffer.begin() + last_tag_prefix_pos + 1, m_value_buffer.end()); - if (!is_valid_uri) { + str_view tag_uri = token.str.substr(last_tag_prefix_pos + 1); + bool is_valid_uri = uri_encoding::validate(tag_uri.begin(), tag_uri.end()); + if FK_YAML_UNLIKELY (!is_valid_uri) { emit_error("Invalid URI character is found in a named tag handle."); } } } - /// @brief Scan a string token, either plain, single-quoted or double-quoted. + /// @brief Scan a scalar token, either plain, single-quoted or double-quoted. + /// @param token The token into which the scan result is written. /// @return lexical_token_t The lexical token type for strings. - lexical_token_t scan_scalar() { + void scan_scalar(lexical_token& token) { m_value_buffer.clear(); bool needs_last_single_quote = false; @@ -647,28 +627,38 @@ class lexical_analyzer { needs_last_double_quote = (*m_cur_itr == '\"'); if (needs_last_double_quote || needs_last_single_quote) { m_token_begin_itr = ++m_cur_itr; + token.type = needs_last_double_quote ? lexical_token_t::DOUBLE_QUOTED_SCALAR + : lexical_token_t::SINGLE_QUOTED_SCALAR; + } + else { + token.type = lexical_token_t::PLAIN_SCALAR; } } - lexical_token_t type = extract_string_token(needs_last_single_quote, needs_last_double_quote); - FK_YAML_ASSERT(type == lexical_token_t::STRING_VALUE); + bool is_value_buff_used = extract_string_token(needs_last_single_quote, needs_last_double_quote); - if (needs_last_single_quote || needs_last_double_quote) { - // just returned the extracted string value if quoted. - return type; + if (is_value_buff_used) { + token.str = str_view {m_value_buffer.begin(), m_value_buffer.end()}; + } + else { + token.str = str_view {m_token_begin_itr, m_cur_itr}; + if (token.type != lexical_token_t::PLAIN_SCALAR) { + // If extract_string_token() didn't use m_value_buffer to store mutated scalar value, m_cur_itr is at + // the last quotation mark, which will cause infinite loops from the next get_next_token() call. + ++m_cur_itr; + } } - - return scalar_scanner::scan(m_value_buffer); } /// @brief Check if the given character is allowed in a single-quoted scalar token. /// @param c The character to be checked. + /// @param is_value_buffer_used true is assigned when mutated scalar contents is written into m_value_buffer. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_single(char c) { - bool ret = false; - + bool is_allowed_single(char c, bool& is_value_buffer_used) { switch (c) { case '\n': { + is_value_buffer_used = true; + // discard trailing white spaces which preceeds the line break in the current line. auto before_trailing_spaces_itr = m_cur_itr - 1; bool ends_loop = false; @@ -706,41 +696,47 @@ class lexical_analyzer { } m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\'') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; + return true; } case '\'': - // If single quotation marks are repeated twice in a single-quoted string token, - // they are considered as an escaped single quotation mark. if (m_cur_itr + 1 == m_end_itr) { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - m_token_begin_itr = m_cur_itr; - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + m_token_begin_itr = m_cur_itr; + } + return false; } if (*(m_cur_itr + 1) != '\'') { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + } + return false; } + // If single quotation marks are repeated twice in a single-quoted string token, + // they are considered as an escaped single quotation mark. + is_value_buffer_used = true; + m_value_buffer.append(m_token_begin_itr, ++m_cur_itr); m_token_begin_itr = m_cur_itr + 1; - ret = true; - break; - } + return true; - return ret; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } } /// @brief Check if the given character is allowed in a double-quoted scalar token. /// @param c The character to be checked. + /// @param is_value_buffer_used true is assigned when mutated scalar contents is written into m_value_buffer. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_double(char c) { - bool ret = false; - + bool is_allowed_double(char c, bool& is_value_buffer_used) { switch (c) { case '\n': { + is_value_buffer_used = true; + // discard trailing white spaces which preceeds the line break in the current line. auto before_trailing_spaces_itr = m_cur_itr - 1; bool ends_loop = false; @@ -778,15 +774,18 @@ class lexical_analyzer { } m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\"') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; + return true; } case '\"': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + } + return false; case '\\': + is_value_buffer_used = true; + m_value_buffer.append(m_token_begin_itr, m_cur_itr); // Handle escaped characters. @@ -795,13 +794,12 @@ class lexical_analyzer { c = *(m_cur_itr + 1); if (c != '\n') { bool is_valid_escaping = yaml_escaper::unescape(m_cur_itr, m_end_itr, m_value_buffer); - if (!is_valid_escaping) { + if FK_YAML_UNLIKELY (!is_valid_escaping) { emit_error("Unsupported escape sequence is found in a string token."); } m_token_begin_itr = m_cur_itr + 1; - ret = true; - break; + return true; } // move until the next non-space character is found. @@ -809,59 +807,91 @@ class lexical_analyzer { skip_white_spaces(); m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\"') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; - } + return true; - return ret; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } } - /// @brief Check if the given character is allowed in a plain scalar token. + /// @brief Check if the given character is allowed in a plain scalar token outside a flow context. /// @param c The character to be checked. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_plain(char c) { - bool ret = false; + bool is_allowed_plain(char c, bool& /*unused*/) { + switch (c) { + case '\n': + return false; + + case ' ': { + // Allow a space in a plain scalar only if the space is surrounded by non-space characters. + // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. + + switch (*(m_cur_itr + 1)) { + case ':': { + // " :" is permitted in a plain style string token, but not when followed by a space. + char peeked = *(m_cur_itr + 2); + if (peeked == ' ') { + return false; + } + return true; + } + case ' ': + case '\n': + case '#': + case '\\': + return false; + } + return true; + } + + case ':': { + // A colon as a key separator must be followed by + // * a white space or + // * a newline code. + switch (*(m_cur_itr + 1)) { + case ' ': + case '\t': + case '\n': + return false; + } + return true; + } + + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } + } + + /// @brief Check if the given character is allowed in a plain scalar token inside a flow context. + /// @param c The character to be checked. + /// @return true if the given character is allowed, false otherwise. + bool is_allowed_plain_flow(char c, bool& /*unused*/) { switch (c) { case '\n': - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; + return false; case ' ': { // Allow a space in an unquoted string only if the space is surrounded by non-space characters. // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. char next = *(m_cur_itr + 1); - bool is_appended = false; // These characters are permitted when not inside a flow collection, and not inside an implicit key. // TODO: Support detection of implicit key context for this check. - if (m_flow_context_depth > 0) { - switch (next) { - case '{': - case '}': - case '[': - case ']': - case ',': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - break; - } - - if (is_appended) { - break; - } + switch (next) { + case '{': + case '}': + case '[': + case ']': + case ',': + return false; } // " :" is permitted in a plain style string token, but not when followed by a space. if (next == ':') { char peeked = *(m_cur_itr + 2); if (peeked == ' ') { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - } - - if (is_appended) { - break; + return false; } } @@ -870,13 +900,10 @@ class lexical_analyzer { case '\n': case '#': case '\\': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - break; + return false; } - ret = !is_appended; - break; + return true; } case ':': { @@ -889,13 +916,9 @@ class lexical_analyzer { case ' ': case '\t': case '\n': - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; - default: - ret = true; - break; + return false; } - break; + return true; } case '{': @@ -903,29 +926,23 @@ class lexical_analyzer { case '[': case ']': case ',': - // just regard the flow indicators as a normal character if plain but not inside a flow context. - if (m_flow_context_depth == 0) { - ret = true; - break; - } + return false; - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE } - - return ret; } /// @brief Extracts a string token, either plain, single-quoted or double-quoted, from the input buffer. - /// @return lexical_token_t The lexical token type for strings. - lexical_token_t extract_string_token(bool needs_last_single_quote, bool needs_last_double_quote) { + /// @return true if mutated scalar contents is stored in m_value_buffer, false otherwise. + bool extract_string_token(bool needs_last_single_quote, bool needs_last_double_quote) { // change behaviors depending on the type of a comming string scalar token. // * single quoted // * double quoted // * plain std::string check_filters {"\n"}; - bool (lexical_analyzer::*pfn_is_allowed)(char) = nullptr; + bool (lexical_analyzer::*pfn_is_allowed)(char, bool&) = nullptr; if (needs_last_single_quote) { check_filters.append("\'"); @@ -935,30 +952,36 @@ class lexical_analyzer { check_filters.append("\"\\"); pfn_is_allowed = &lexical_analyzer::is_allowed_double; } - else // plain scalars - { - check_filters.append(" :{}[],"); + else if (m_flow_context_depth == 0) { + // plain scalar outside flow contexts + check_filters.append(" :"); pfn_is_allowed = &lexical_analyzer::is_allowed_plain; } + else { + // plain scalar inside flow contexts + check_filters.append(" :{}[],"); + pfn_is_allowed = &lexical_analyzer::is_allowed_plain_flow; + } // scan the contents of a string scalar token. - for (; m_cur_itr != m_end_itr; m_cur_itr = (m_cur_itr == m_end_itr) ? m_cur_itr : ++m_cur_itr) { + bool is_value_buffer_used = false; + for (; m_cur_itr != m_end_itr; ++m_cur_itr) { char current = *m_cur_itr; uint32_t num_bytes = utf8::get_num_bytes(static_cast(current)); - if (num_bytes == 1) { + if FK_YAML_LIKELY (num_bytes == 1) { auto ret = check_filters.find(current); if (ret != std::string::npos) { - bool is_allowed = (this->*pfn_is_allowed)(current); + bool is_allowed = (this->*pfn_is_allowed)(current, is_value_buffer_used); if (!is_allowed) { - return lexical_token_t::STRING_VALUE; + return is_value_buffer_used; } continue; } // Handle unescaped control characters. - if (static_cast(current) <= 0x1F) { + if FK_YAML_UNLIKELY (static_cast(current) <= 0x1F) { handle_unescaped_control_char(current); continue; } @@ -973,25 +996,22 @@ class lexical_analyzer { // Handle the end of input buffer. - if (needs_last_double_quote) { + if FK_YAML_UNLIKELY (needs_last_double_quote) { emit_error("Invalid end of input buffer in a double-quoted string token."); } - if (needs_last_single_quote) { + if FK_YAML_UNLIKELY (needs_last_single_quote) { emit_error("Invalid end of input buffer in a single-quoted string token."); } - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - return lexical_token_t::STRING_VALUE; + return is_value_buffer_used; } /// @brief Scan a block style string token either in the literal or folded style. /// @param style The style of the given token, either literal or folded. /// @param chomp The chomping indicator type of the given token, either strip, keep or clip. /// @param indent The indent size specified for the given token. - /// @return The lexical token type for strings. - lexical_token_t scan_block_style_string_token( - block_style_indicator_t style, chomping_indicator_t chomp, uint32_t indent) { + void scan_block_style_string_token(block_style_indicator_t style, chomping_indicator_t chomp, uint32_t indent) { m_value_buffer.clear(); // Handle leading all-space lines. @@ -1014,7 +1034,7 @@ class lexical_analyzer { if (chomp != chomping_indicator_t::KEEP) { m_value_buffer.clear(); } - return lexical_token_t::STRING_VALUE; + return; } m_pos_tracker.update_position(m_cur_itr); @@ -1024,7 +1044,7 @@ class lexical_analyzer { if (indent == 0) { indent = cur_indent; } - else if (cur_indent < indent) { + else if FK_YAML_UNLIKELY (cur_indent < indent) { emit_error("A block style scalar is less indented than the indicated level."); } @@ -1038,13 +1058,13 @@ class lexical_analyzer { } uint32_t diff = cur_indent - indent; - // m_value_buffer.append(diff, ' '); m_token_begin_itr -= diff; - chars_in_line += diff; + chars_in_line = diff; } for (; m_cur_itr != m_end_itr; ++m_cur_itr) { - if (*m_cur_itr == '\n') { + char current = *m_cur_itr; + if (current == '\n') { if (style == block_style_indicator_t::LITERAL) { if (chars_in_line == 0) { m_value_buffer.push_back('\n'); @@ -1074,12 +1094,12 @@ class lexical_analyzer { break; } - char current = *m_cur_itr; - if (current == ' ') { + char c = *m_cur_itr; + if (c == ' ') { continue; } - if (current == '\n') { + if (c == '\n') { is_next_empty = true; break; } @@ -1099,8 +1119,6 @@ class lexical_analyzer { chars_in_line = 0; continue; } - else { - } switch (char next = *(m_cur_itr + 1)) { case '\n': @@ -1129,7 +1147,7 @@ class lexical_analyzer { m_pos_tracker.update_position(m_cur_itr); cur_indent = m_pos_tracker.get_cur_pos_in_line(); if (cur_indent < indent) { - if (*m_cur_itr != ' ') { + if (current != ' ') { // Interpret less indented non-space characters as the start of the next token. break; } @@ -1137,7 +1155,7 @@ class lexical_analyzer { continue; } - if (*m_cur_itr == ' ' && style == block_style_indicator_t::FOLDED) { + if (current == ' ' && style == block_style_indicator_t::FOLDED) { // A line being more indented is not folded. m_value_buffer.push_back('\n'); is_extra_indented = true; @@ -1145,7 +1163,6 @@ class lexical_analyzer { m_token_begin_itr = m_cur_itr; } - // m_value_buffer.push_back(current); ++chars_in_line; } @@ -1171,21 +1188,21 @@ class lexical_analyzer { // No need to chomp the trailing newlines. break; } - while (m_value_buffer.size() > 1) { + uint32_t buf_size = static_cast(m_value_buffer.size()); + while (buf_size > 1) { // Strings with only newlines are handled above, so no check for the case. - char second_last = *(m_value_buffer.end() - 2); + char second_last = m_value_buffer[buf_size - 2]; if (second_last != '\n') { break; } m_value_buffer.pop_back(); + --buf_size; } break; } case chomping_indicator_t::KEEP: break; } - - return lexical_token_t::STRING_VALUE; } /// @brief Handle unescaped control characters. @@ -1264,30 +1281,53 @@ class lexical_analyzer { /// @param indent A variable to store the retrieved indent size. void get_block_style_metadata(chomping_indicator_t& chomp_type, uint32_t& indent) { chomp_type = chomping_indicator_t::CLIP; - switch (*++m_cur_itr) { - case '-': - chomp_type = chomping_indicator_t::STRIP; - ++m_cur_itr; - break; - case '+': - chomp_type = chomping_indicator_t::KEEP; - ++m_cur_itr; - break; - default: - break; - } + indent = 0; - if (*m_cur_itr == '0') { - emit_error("An indentation level for a block style scalar cannot be \'0\'"); - } + while (m_cur_itr != m_end_itr) { + switch (*m_cur_itr) { + case '-': + if FK_YAML_UNLIKELY (chomp_type != chomping_indicator_t::CLIP) { + emit_error("Too many block chomping indicators specified."); + } + chomp_type = chomping_indicator_t::STRIP; + break; + case '+': + if FK_YAML_UNLIKELY (chomp_type != chomping_indicator_t::CLIP) { + emit_error("Too many block chomping indicators specified."); + } + chomp_type = chomping_indicator_t::KEEP; + break; + case '0': + emit_error("An indentation level for a block scalar cannot be 0."); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if FK_YAML_UNLIKELY (indent > 0) { + emit_error("Invalid indentation level for a block scalar. It must be between 1 and 9."); + } + indent = static_cast(*m_cur_itr - '0'); + break; + case ' ': + case '\t': + break; + case '\n': + ++m_cur_itr; + return; + case '#': + skip_until_line_end(); + return; + default: + emit_error("Invalid character found in a block scalar header."); + } - indent = 0; - if (std::isdigit(*m_cur_itr)) { - indent = static_cast(*m_cur_itr++ - '0'); + ++m_cur_itr; } - - // skip characters including comments. - skip_until_line_end(); } /// @brief Skip white spaces (half-width spaces and tabs) from the current position. @@ -1334,21 +1374,23 @@ class lexical_analyzer { private: /// An input buffer adapter to be analyzed. - std::string m_input_buffer {}; + str_view m_input_buffer {}; /// The iterator to the current character in the input buffer. - std::string::const_iterator m_cur_itr {}; + const char* m_cur_itr {}; /// The iterator to the beginning of the current token. - std::string::const_iterator m_token_begin_itr {}; + const char* m_token_begin_itr {}; /// The iterator to the past-the-end element in the input buffer. - std::string::const_iterator m_end_itr {}; + const char* m_end_itr {}; /// The current position tracker of the input buffer. mutable position_tracker m_pos_tracker {}; /// A temporal buffer to store a string to be parsed to an actual token value. std::string m_value_buffer {}; + /// The last yaml version. + str_view m_yaml_version {}; /// The last tag handle. - std::string m_tag_handle {}; - /// The last tag prefix - std::string m_tag_prefix {}; + str_view m_tag_handle {}; + /// The last tag prefix. + str_view m_tag_prefix {}; /// The beginning position of the last lexical token. (zero origin) uint32_t m_last_token_begin_pos {0}; /// The beginning line of the last lexical token. (zero origin) diff --git a/include/fkYAML/detail/input/position_tracker.hpp b/include/fkYAML/detail/input/position_tracker.hpp index 8e20e9a4..0f8f5ae8 100644 --- a/include/fkYAML/detail/input/position_tracker.hpp +++ b/include/fkYAML/detail/input/position_tracker.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -12,20 +12,16 @@ #define FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ #include -#include -#include -#include #include -#include -#include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN /// @brief A position tracker of the target buffer. class position_tracker { public: - void set_target_buffer(const std::string& buffer) { + void set_target_buffer(str_view buffer) noexcept { m_begin = m_last = buffer.begin(); m_end = buffer.end(); } @@ -33,19 +29,26 @@ class position_tracker { /// @brief Update the set of the current position informations. /// @note This function doesn't support cases where cur_pos has moved backward from the last call. /// @param cur_pos The iterator to the current element of the buffer. - void update_position(std::string::const_iterator cur_pos) { - m_cur_pos = static_cast(std::distance(m_begin, cur_pos)); - m_lines_read += static_cast(std::count(m_last, cur_pos, '\n')); - m_last = cur_pos; + void update_position(const char* p_current) { + uint32_t diff = static_cast(p_current - m_last); + if (diff == 0) { + return; + } + + m_cur_pos += diff; + uint32_t prev_lines_read = m_lines_read; + m_lines_read += static_cast(std::count(m_last, p_current, '\n')); + m_last = p_current; - if (m_lines_read == 0) { - m_cur_pos_in_line = m_cur_pos; + if (prev_lines_read == m_lines_read) { + m_cur_pos_in_line += diff; return; } uint32_t count = 0; - while (--cur_pos != m_begin) { - if (*cur_pos == '\n') { + const char* p_begin = m_begin; + while (--p_current != p_begin) { + if (*p_current == '\n') { break; } count++; @@ -71,11 +74,11 @@ class position_tracker { private: /// The iterator to the beginning element in the target buffer. - std::string::const_iterator m_begin {}; + const char* m_begin {}; /// The iterator to the past-the-end element in the target buffer. - std::string::const_iterator m_end {}; + const char* m_end {}; /// The iterator to the last updated element in the target buffer. - std::string::const_iterator m_last {}; + const char* m_last {}; /// The current position from the beginning of an input buffer. uint32_t m_cur_pos {0}; /// The current position in the current line. diff --git a/include/fkYAML/detail/input/scalar_scanner.hpp b/include/fkYAML/detail/input/scalar_scanner.hpp index 3668e94a..a9d8aa31 100644 --- a/include/fkYAML/detail/input/scalar_scanner.hpp +++ b/include/fkYAML/detail/input/scalar_scanner.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -11,12 +11,12 @@ #ifndef FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ #define FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ -#include +#include #include #include #include -#include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -39,72 +39,109 @@ inline bool is_xdigit(char c) { } } // namespace + +/// @brief The class which detects a scalar value type by scanning contents. class scalar_scanner { public: - static lexical_token_t scan(const std::string& token) { - switch (token.size()) { - case 0: - return lexical_token_t::STRING_VALUE; + /// @brief Detects a scalar value type by scanning the contents ranged by the given iterators. + /// @param begin The iterator to the first element of the scalar. + /// @param end The iterator to the past-the-end element of the scalar. + /// @return A detected scalar value type. + static node_type scan(const char* begin, const char* end) { + if (begin == end) { + return node_type::STRING; + } + + uint32_t len = static_cast(std::distance(begin, end)); + if (len > 5) { + return scan_possible_number_token(begin, len); + } + + const char* p_begin = &*begin; + + switch (len) { case 1: - if (token[0] == '~') { - return lexical_token_t::NULL_VALUE; + if (*p_begin == '~') { + return node_type::NULL_OBJECT; } break; case 4: - switch (token[0]) { + switch (*p_begin) { case 'n': + // no possible case of begin a number otherwise. + return (std::strncmp(p_begin + 1, "ull", 3) == 0) ? node_type::NULL_OBJECT : node_type::STRING; case 'N': - if (token == "null" || token == "Null" || token == "NULL") { - return lexical_token_t::NULL_VALUE; - } - break; + // no possible case of begin a number otherwise. + return ((std::strncmp(p_begin + 1, "ull", 3) == 0) || (std::strncmp(p_begin + 1, "ULL", 3) == 0)) + ? node_type::NULL_OBJECT + : node_type::STRING; case 't': + // no possible case of being a number otherwise. + return (std::strncmp(p_begin + 1, "rue", 3) == 0) ? node_type::BOOLEAN : node_type::STRING; case 'T': - if (token == "true" || token == "True" || token == "TRUE") { - return lexical_token_t::BOOLEAN_VALUE; - } - break; - case '.': - if (token == ".inf" || token == ".Inf" || token == ".INF" || token == ".nan" || token == ".NaN" || - token == ".NAN") { - return lexical_token_t::FLOAT_NUMBER_VALUE; + // no possible case of being a number otherwise. + return ((std::strncmp(p_begin + 1, "rue", 3) == 0) || (std::strncmp(p_begin + 1, "RUE", 3) == 0)) + ? node_type::BOOLEAN + : node_type::STRING; + case '.': { + const char* p_from_second = p_begin + 1; + bool is_inf_or_nan_scalar = + (std::strncmp(p_from_second, "inf", 3) == 0) || (std::strncmp(p_from_second, "Inf", 3) == 0) || + (std::strncmp(p_from_second, "INF", 3) == 0) || (std::strncmp(p_from_second, "nan", 3) == 0) || + (std::strncmp(p_from_second, "NaN", 3) == 0) || (std::strncmp(p_from_second, "NAN", 3) == 0); + if (is_inf_or_nan_scalar) { + return node_type::FLOAT; } + // maybe a number. break; } + } break; case 5: - switch (token[0]) { + switch (*p_begin) { case 'f': + // no possible case of being a number otherwise. + return (std::strncmp(p_begin + 1, "alse", 4) == 0) ? node_type::BOOLEAN : node_type::STRING; case 'F': - if (token == "false" || token == "False" || token == "FALSE") { - return lexical_token_t::BOOLEAN_VALUE; - } - break; + // no possible case of being a number otherwise. + return ((std::strncmp(p_begin + 1, "alse", 4) == 0) || (std::strncmp(p_begin + 1, "ALSE", 4) == 0)) + ? node_type::BOOLEAN + : node_type::STRING; + case '+': case '-': - if (token[1] == '.' && (token == "-.inf" || token == "-.Inf" || token == "-.INF")) { - return lexical_token_t::FLOAT_NUMBER_VALUE; + if (*(p_begin + 1) == '.') { + const char* p_from_third = p_begin + 2; + bool is_min_inf_scalar = (std::strncmp(p_from_third, "inf", 3) == 0) || + (std::strncmp(p_from_third, "Inf", 3) == 0) || + (std::strncmp(p_from_third, "INF", 3) == 0); + if (is_min_inf_scalar) { + return node_type::FLOAT; + } } + // maybe a number. break; } break; } - return scan_possible_number_token(token); + return scan_possible_number_token(begin, len); } private: - static lexical_token_t scan_possible_number_token(const std::string& token) { - std::string::const_iterator itr = token.begin(); - std::size_t size = token.size(); - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type from the contents (possibly an integer or a floating-point value). + /// @param itr The iterator to the first element of the scalar. + /// @param len The length of the scalar contents. + /// @return A detected scalar value type. + static node_type scan_possible_number_token(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); switch (*itr) { case '-': - return (size > 1) ? scan_negative_number(++itr, --size) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_negative_number(++itr, --len) : node_type::STRING; case '+': - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::STRING; case '0': - return (size > 1) ? scan_after_zero_at_first(++itr, --size) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_after_zero_at_first(++itr, --len) : node_type::INTEGER; case '1': case '2': case '3': @@ -114,112 +151,132 @@ class scalar_scanner { case '7': case '8': case '9': - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::INTEGER; default: - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } } - static lexical_token_t scan_negative_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents right after the negative sign. + /// @param itr The iterator to the past-the-negative-sign element of the scalar. + /// @param len The length of the scalar contents left unscanned. + /// @return A detected scalar value type. + static node_type scan_negative_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::INTEGER; } - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } - static lexical_token_t scan_after_zero_at_first(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents right after the beginning 0. + /// @param itr The iterator to the past-the-zero element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_after_zero_at_first(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); if (is_digit(*itr)) { // a token consisting of the beginning '0' and some following numbers, e.g., `0123`, is not an integer // according to https://yaml.org/spec/1.2.2/#10213-integer. - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } switch (*itr) { case '.': { - if (size == 1) { + if (len == 1) { // 0 is omitted after `0.`. - return lexical_token_t::FLOAT_NUMBER_VALUE; + return node_type::FLOAT; } - lexical_token_t ret = scan_after_decimal_point(++itr, --size, true); - return (ret == lexical_token_t::STRING_VALUE) ? lexical_token_t::STRING_VALUE - : lexical_token_t::FLOAT_NUMBER_VALUE; + node_type ret = scan_after_decimal_point(++itr, --len, true); + return (ret == node_type::STRING) ? node_type::STRING : node_type::FLOAT; } case 'o': - return (size > 1) ? scan_octal_number(++itr, --size) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_octal_number(++itr, --len) : node_type::STRING; case 'x': - return (size > 1) ? scan_hexadecimal_number(++itr, --size) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::STRING; default: - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } } - static lexical_token_t scan_decimal_number( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents part starting with a decimal. + /// @param itr The iterator to the beginning decimal element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether a decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_decimal_number(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::INTEGER; } switch (*itr) { case '.': { if (has_decimal_point) { // the token has more than one period, e.g., a semantic version `1.2.3`. - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } - if (size == 1) { + if (len == 1) { // 0 is omitted after the decimal point - return lexical_token_t::FLOAT_NUMBER_VALUE; + return node_type::FLOAT; } - lexical_token_t ret = scan_after_decimal_point(++itr, --size, true); - return (ret == lexical_token_t::STRING_VALUE) ? lexical_token_t::STRING_VALUE - : lexical_token_t::FLOAT_NUMBER_VALUE; + node_type ret = scan_after_decimal_point(++itr, --len, true); + return (ret == node_type::STRING) ? node_type::STRING : node_type::FLOAT; } case 'e': case 'E': - return (size > 1) ? scan_after_exponent(++itr, --size, has_decimal_point) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_after_exponent(++itr, --len, has_decimal_point) : node_type::STRING; default: - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } } - static lexical_token_t scan_after_decimal_point( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents right after a decimal point. + /// @param itr The iterator to the past-the-decimal-point element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether the decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_after_decimal_point(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) - : lexical_token_t::FLOAT_NUMBER_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::FLOAT; } - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } - static lexical_token_t scan_after_exponent( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents right after the exponent prefix ("e" or "E"). + /// @param itr The iterator to the past-the-exponent-prefix element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether the decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_after_exponent(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) - : lexical_token_t::FLOAT_NUMBER_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::FLOAT; } switch (*itr) { case '+': case '-': - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) : lexical_token_t::STRING_VALUE; + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::STRING; default: - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } } - static lexical_token_t scan_octal_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents assuming octal numbers. + /// @param itr The iterator to the octal-number element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_octal_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); switch (*itr) { case '0': @@ -230,19 +287,23 @@ class scalar_scanner { case '5': case '6': case '7': - return (size > 1) ? scan_octal_number(++itr, --size) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_octal_number(++itr, --len) : node_type::INTEGER; default: - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } } - static lexical_token_t scan_hexadecimal_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Detects a scalar value type by scanning the contents assuming hexadecimal numbers. + /// @param itr The iterator to the hexadecimal-number element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_hexadecimal_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); if (is_xdigit(*itr)) { - return (size > 1) ? scan_hexadecimal_number(++itr, --size) : lexical_token_t::INTEGER_VALUE; + return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::INTEGER; } - return lexical_token_t::STRING_VALUE; + return node_type::STRING; } }; diff --git a/include/fkYAML/detail/input/tag_resolver.hpp b/include/fkYAML/detail/input/tag_resolver.hpp index 9185b571..a590e6d1 100644 --- a/include/fkYAML/detail/input/tag_resolver.hpp +++ b/include/fkYAML/detail/input/tag_resolver.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -49,10 +49,10 @@ class tag_resolver { private: static std::string normalize_tag_name( const std::string& tag, const std::shared_ptr& directives) { - if (tag.empty()) { + if FK_YAML_UNLIKELY (tag.empty()) { throw invalid_tag("tag must not be empty.", ""); } - if (tag[0] != '!') { + if FK_YAML_UNLIKELY (tag[0] != '!') { throw invalid_tag("tag must start with \'!\'", tag.c_str()); } @@ -100,14 +100,14 @@ class tag_resolver { FK_YAML_ASSERT(tag_end_pos < tag.size() - 1); bool is_null_or_empty = !directives || directives->named_handle_map.empty(); - if (is_null_or_empty) { + if FK_YAML_UNLIKELY (is_null_or_empty) { throw invalid_tag("named handle has not been registered.", tag.c_str()); } // find the extracted named handle in the map. auto named_handle_itr = directives->named_handle_map.find(tag.substr(0, tag_end_pos + 1)); auto end_itr = directives->named_handle_map.end(); - if (named_handle_itr == end_itr) { + if FK_YAML_UNLIKELY (named_handle_itr == end_itr) { throw invalid_tag("named handle has not been registered.", tag.c_str()); } diff --git a/include/fkYAML/detail/input/tag_t.hpp b/include/fkYAML/detail/input/tag_t.hpp index 6b9b89ad..8277405c 100644 --- a/include/fkYAML/detail/input/tag_t.hpp +++ b/include/fkYAML/detail/input/tag_t.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/iterator.hpp b/include/fkYAML/detail/iterator.hpp index 432f92f9..9e575345 100644 --- a/include/fkYAML/detail/iterator.hpp +++ b/include/fkYAML/detail/iterator.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -150,7 +150,7 @@ class iterator { /// @param rhs An iterator object to be copied with. /// @return iterator& Reference to this iterator object. iterator& operator=(const iterator& rhs) noexcept { - if (&rhs == this) { + if FK_YAML_UNLIKELY (&rhs == this) { return *this; } @@ -171,7 +171,7 @@ class iterator { /// @param rhs An iterator object to be moved from. /// @return iterator& Reference to this iterator object. iterator& operator=(iterator&& rhs) noexcept { - if (&rhs == this) { + if FK_YAML_UNLIKELY (&rhs == this) { return *this; } @@ -299,7 +299,7 @@ class iterator { /// @return true This iterator object is equal to the other. /// @return false This iterator object is not equal to the other. bool operator==(const iterator& rhs) const { - if (m_inner_iterator_type != rhs.m_inner_iterator_type) { + if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) { throw fkyaml::exception("Cannot compare iterators of different container types."); } @@ -324,11 +324,11 @@ class iterator { /// @return true This iterator object is less than the other. /// @return false This iterator object is not less than the other. bool operator<(const iterator& rhs) const { - if (m_inner_iterator_type != rhs.m_inner_iterator_type) { + if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) { throw fkyaml::exception("Cannot compare iterators of different container types."); } - if (m_inner_iterator_type == iterator_t::MAPPING) { + if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::MAPPING) { throw fkyaml::exception("Cannot compare order of iterators of the mapping container type"); } @@ -369,7 +369,7 @@ class iterator { /// @brief Get the key string of the YAML mapping node for the current iterator. /// @return const std::string& The key string of the YAML mapping node for the current iterator. const typename ValueType::mapping_type::key_type& key() const { - if (m_inner_iterator_type == iterator_t::SEQUENCE) { + if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::SEQUENCE) { throw fkyaml::exception("Cannot retrieve key from non-mapping iterators."); } diff --git a/include/fkYAML/detail/macros/cpp_config_macros.hpp b/include/fkYAML/detail/macros/cpp_config_macros.hpp index e8711fb8..a466978c 100644 --- a/include/fkYAML/detail/macros/cpp_config_macros.hpp +++ b/include/fkYAML/detail/macros/cpp_config_macros.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -14,18 +14,27 @@ // This file is assumed to be included only by version_macros.hpp file. // To avoid redundant inclusion, do not include version_macros.hpp file as the other files do. +// With the MSVC compilers, the value of __cplusplus is by default always "199611L"(C++98). +// To avoid that, the library instead references _MSVC_LANG which is always set a correct value. +// See https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ for more details. +#if defined(_MSVC_LANG) && !defined(__clang__) +#define FK_YAML_CPLUSPLUS _MSVC_LANG +#else +#define FK_YAML_CPLUSPLUS __cplusplus +#endif + // C++ language standard detection (__cplusplus is not yet defined for C++23) // Skip detection if the definitions listed below already exist. #if !defined(FK_YAML_HAS_CXX_20) && !defined(FK_YAML_HAS_CXX_17) && !defined(FK_YAML_HAS_CXX_14) && \ !defined(FK_YAML_CXX_11) -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && MSVC_LANG >= 202002L) +#if FK_YAML_CPLUSPLUS >= 202002L #define FK_YAML_HAS_CXX_20 #define FK_YAML_HAS_CXX_17 #define FK_YAML_HAS_CXX_14 -#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) +#elif FK_YAML_CPLUSPLUS >= 201703L #define FK_YAML_HAS_CXX_17 #define FK_YAML_HAS_CXX_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) +#elif FK_YAML_CPLUSPLUS >= 201402L #define FK_YAML_HAS_CXX_14 #endif @@ -33,27 +42,90 @@ #define FK_YAML_HAS_CXX_11 #endif -// switch usage of inline variables. Inline variables have been introduced since C++17. +// switch usage of the deprecated attribute. [[deprecated]] is available since C++14. +#if defined(FK_YAML_HAS_CXX_14) +#define FK_YAML_DEPRECATED(msg) [[deprecated(msg)]] +#else +#if defined(_MSC_VER) +#define FK_YAML_DEPRECATED(msg) __declspec(deprecated(msg)) +#elif defined(__GNUC__) || defined(__clang__) +#define FK_YAML_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define FK_YAML_DEPRECATED(msg) +#endif +#endif + +// switch usage of inline variables which have been available since C++17. #if defined(FK_YAML_HAS_CXX_17) #define FK_YAML_INLINE_VAR inline #else #define FK_YAML_INLINE_VAR #endif +// Detect __has_* macros. +// The following macros replace redundant `defined(__has_*) && __has_*(...)`. + #ifdef __has_include -#if __has_include() +#define FK_YAML_HAS_INCLUDE(header) __has_include(header) +#else +#define FK_YAML_HAS_INCLUDE(header) (0) +#endif + +#ifdef __has_builtin +#define FK_YAML_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +#define FK_YAML_HAS_BUILTIN(builtin) (0) +#endif + +#ifdef __has_cpp_attribute +#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) (0) +#endif + +#if FK_YAML_HAS_INCLUDE() // is available since C++20 #include #endif + +// +// C++ feature detections +// + +// switch usages of the std::to_chars()/std::from_chars() functions which have been available since C++17. +#if defined(FK_YAML_HAS_CXX_17) && defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L +#define FK_YAML_HAS_TO_CHARS (1) +#else +#define FK_YAML_HAS_TO_CHARS (0) #endif -// switch usage of char8_t. char8_t has been introduced since C++20 -#if !defined(FK_YAML_HAS_CHAR8_T) -#if defined(FK_YAML_HAS_CXX_20) -#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L -#define FK_YAML_HAS_CHAR8_T +// switch usage of char8_t which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && defined(__cpp_char8_t) && __cpp_char8_t >= 201811L +#define FK_YAML_HAS_CHAR8_T (1) +#else +#define FK_YAML_HAS_CHAR8_T (0) #endif + +// +// C++ attribute detections +// + +// switch usage of [[likely]] C++ attribute which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(likely) >= 201803L +#define FK_YAML_LIKELY(expr) (!!(expr)) [[likely]] +#elif FK_YAML_HAS_BUILTIN(__builtin_expect) +#define FK_YAML_LIKELY(expr) (__builtin_expect(!!(expr), 1)) +#else +#define FK_YAML_LIKELY(expr) (!!(expr)) #endif + +// switch usage of [[unlikely]] C++ attribute which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(unlikely) >= 201803L +#define FK_YAML_UNLIKELY(expr) (!!(expr)) [[unlikely]] +#elif FK_YAML_HAS_BUILTIN(__builtin_expect) +#define FK_YAML_UNLIKELY(expr) (__builtin_expect(!!(expr), 0)) +#else +#define FK_YAML_UNLIKELY(expr) (!!(expr)) #endif #endif /* FK_YAML_DETAIL_MACROS_CPP_CONFIG_MACROS_HPP_ */ diff --git a/include/fkYAML/detail/macros/version_macros.hpp b/include/fkYAML/detail/macros/version_macros.hpp index 37e97e87..9b35990d 100644 --- a/include/fkYAML/detail/macros/version_macros.hpp +++ b/include/fkYAML/detail/macros/version_macros.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,7 +10,7 @@ // Check version definitions if already defined. #if defined(FK_YAML_MAJOR_VERSION) && defined(FK_YAML_MINOR_VERSION) && defined(FK_YAML_PATCH_VERSION) -#if FK_YAML_MAJOR_VERSION != 0 || FK_YAML_MINOR_VERSION != 3 || FK_YAML_PATCH_VERSION != 11 +#if FK_YAML_MAJOR_VERSION != 0 || FK_YAML_MINOR_VERSION != 3 || FK_YAML_PATCH_VERSION != 12 #warning Already included a different version of the fkYAML library! #else // define macros to skip defining macros down below. @@ -22,7 +22,7 @@ #define FK_YAML_MAJOR_VERSION 0 #define FK_YAML_MINOR_VERSION 3 -#define FK_YAML_PATCH_VERSION 11 +#define FK_YAML_PATCH_VERSION 12 #define FK_YAML_NAMESPACE_VERSION_CONCAT_IMPL(major, minor, patch) v##major##_##minor##_##patch diff --git a/include/fkYAML/detail/meta/detect.hpp b/include/fkYAML/detail/meta/detect.hpp index fb6cda99..56e30e48 100644 --- a/include/fkYAML/detail/meta/detect.hpp +++ b/include/fkYAML/detail/meta/detect.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/meta/input_adapter_traits.hpp b/include/fkYAML/detail/meta/input_adapter_traits.hpp index 20dbfe77..359b84c5 100644 --- a/include/fkYAML/detail/meta/input_adapter_traits.hpp +++ b/include/fkYAML/detail/meta/input_adapter_traits.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -11,34 +11,40 @@ #ifndef FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ #define FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ +#include #include #include +#include #include #include #include +#if defined(FK_YAML_HAS_CXX_17) && FK_YAML_HAS_INCLUDE() +#include +#endif + FK_YAML_DETAIL_NAMESPACE_BEGIN /////////////////////////////////////////// // Input Adapter API detection traits /////////////////////////////////////////// -/// @brief A type which represents get_character function. +/// @brief A type which represents get_buffer_view function. /// @tparam T A target type. template -using fill_buffer_fn_t = decltype(std::declval().fill_buffer(std::declval())); +using get_buffer_view_fn_t = decltype(std::declval().get_buffer_view()); -/// @brief Type traits to check if InputAdapterType has get_character member function. -/// @tparam InputAdapterType An input adapter type to check if it has get_character function. +/// @brief Type traits to check if InputAdapterType has get_buffer_view member function. +/// @tparam InputAdapterType An input adapter type to check if it has get_buffer_view function. /// @tparam typename N/A template -struct has_fill_buffer : std::false_type {}; +struct has_get_buffer_view : std::false_type {}; -/// @brief A partial specialization of has_fill_buffer if InputAdapterType has get_character member function. +/// @brief A partial specialization of has_get_buffer_view if InputAdapterType has get_buffer_view member function. /// @tparam InputAdapterType A type of a target input adapter. template -struct has_fill_buffer::value>> +struct has_get_buffer_view::value>> : std::true_type {}; //////////////////////////////// @@ -54,7 +60,46 @@ struct is_input_adapter : std::false_type {}; /// @brief A partial specialization of is_input_adapter if T is an input adapter type. /// @tparam InputAdapterType template -struct is_input_adapter::value>> : std::true_type {}; +struct is_input_adapter::value>> : std::true_type { +}; + +///////////////////////////////////////////////// +// traits for contiguous iterator detection +///////////////////////////////////////////////// + +/// @brief Type traits to check if T is a container which has contiguous bytes. +/// @tparam T A target type. +template +struct is_contiguous_container : std::false_type {}; + +/// @brief A partial specialization of is_contiguous_container if T is a std::array. +/// @tparam T Element type. +/// @tparam N Maximum number of elements. +template +struct is_contiguous_container> : std::true_type {}; + +/// @brief A partial specialization of is_contiguous_container if T is a std::basic_string. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +/// @tparam Alloc Allocator type. +template +struct is_contiguous_container> : std::true_type {}; + +#ifdef FK_YAML_HAS_CXX_17 + +/// @brief A partial specialization of is_contiguous_container if T is a std::basic_string_view. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +template +struct is_contiguous_container> : std::true_type {}; + +#endif // defined(FK_YAML_HAS_CXX_20) + +/// @brief A partial specialization of is_contiguous_container if T is a std::vector. +/// @tparam T Element type. +/// @tparam Alloc Allocator type. +template +struct is_contiguous_container> : std::true_type {}; FK_YAML_DETAIL_NAMESPACE_END diff --git a/include/fkYAML/detail/meta/node_traits.hpp b/include/fkYAML/detail/meta/node_traits.hpp index e50e4745..bb64befa 100644 --- a/include/fkYAML/detail/meta/node_traits.hpp +++ b/include/fkYAML/detail/meta/node_traits.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/meta/stl_supplement.hpp b/include/fkYAML/detail/meta/stl_supplement.hpp index 945f2c26..481e9836 100644 --- a/include/fkYAML/detail/meta/stl_supplement.hpp +++ b/include/fkYAML/detail/meta/stl_supplement.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/meta/type_traits.hpp b/include/fkYAML/detail/meta/type_traits.hpp index 46bafe16..b1307477 100644 --- a/include/fkYAML/detail/meta/type_traits.hpp +++ b/include/fkYAML/detail/meta/type_traits.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -11,6 +11,7 @@ #ifndef FK_YAML_DETAIL_META_TYPE_TRAITS_HPP_ #define FK_YAML_DETAIL_META_TYPE_TRAITS_HPP_ +#include #include #include @@ -105,6 +106,12 @@ struct is_complete_type : std::false_type {}; template struct is_complete_type : std::true_type {}; +/// @brief A utility alias to test if the value type of `ItrType` is `T`. +/// @tparam ItrType An iterator type. +/// @tparam T The target iterator value type. +template +using is_iterator_of = std::is_same::value_type>, T>; + /// @brief A utility struct to generate static constant instance. /// @tparam T A target type for the resulting static constant instance. template diff --git a/include/fkYAML/detail/node_attrs.hpp b/include/fkYAML/detail/node_attrs.hpp new file mode 100644 index 00000000..414be7b5 --- /dev/null +++ b/include/fkYAML/detail/node_attrs.hpp @@ -0,0 +1,139 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_NODE_ATTRS_HPP_ +#define FK_YAML_DETAIL_NODE_ATTRS_HPP_ + +#include +#include + +#include +#include + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief The type for node attribute bits. +using node_attr_t = uint32_t; + +/// @brief The namespace to define bit masks for node attribute bits. +namespace node_attr_mask { + +/// The bit mask for node value type bits. +const node_attr_t value = 0x0000FFFFu; +/// The bit mask for node style type bits. (bits are not yet defined.) +const node_attr_t style = 0x00FF0000u; +/// The bit mask for node property related bits. +const node_attr_t props = 0xFF000000u; +/// The bit mask for anchor/alias node type bits. +const node_attr_t anchoring = 0x03000000u; +/// The bit mask for anchor offset value bits. +const node_attr_t anchor_offset = 0xFC000000u; +/// The bit mask for all the bits for node attributes. +const node_attr_t all = std::numeric_limits::max(); + +} // namespace node_attr_mask + +/// @brief The namespace to define bits for node attributes. +namespace node_attr_bits { + +/// The sequence node bit. +const node_attr_t seq_bit = 1u << 0; +/// The mapping node bit. +const node_attr_t map_bit = 1u << 1; +/// The null scalar node bit. +const node_attr_t null_bit = 1u << 2; +/// The boolean scalar node bit. +const node_attr_t bool_bit = 1u << 3; +/// The integer scalar node bit. +const node_attr_t int_bit = 1u << 4; +/// The floating point scalar node bit. +const node_attr_t float_bit = 1u << 5; +/// The string scalar node bit. +const node_attr_t string_bit = 1u << 6; + +/// A utility bit set to filter scalar node bits. +const node_attr_t scalar_bits = null_bit | bool_bit | int_bit | float_bit | string_bit; + +/// The anchor node bit. +const node_attr_t anchor_bit = 0x01000000u; +/// The alias node bit. +const node_attr_t alias_bit = 0x02000000u; + +/// A utility bit set for initialization. +const node_attr_t default_bits = null_bit; + +/// @brief Converts a node_type value to a node_attr_t value. +/// @param t A type of node value. +/// @return The associated node value bit. +inline node_attr_t from_node_type(node_type t) noexcept { + switch (t) { + case node_type::SEQUENCE: + return seq_bit; + case node_type::MAPPING: + return map_bit; + case node_type::NULL_OBJECT: + return null_bit; + case node_type::BOOLEAN: + return bool_bit; + case node_type::INTEGER: + return int_bit; + case node_type::FLOAT: + return float_bit; + case node_type::STRING: + return string_bit; + default: // LCOV_EXCL_LINE + return node_attr_mask::all; // LCOV_EXCL_LINE + } +} + +/// @brief Converts a node_attr_t value to a node_type value. +/// @param bits node attribute bits +/// @return An associated node value type with the given node value bit. +inline node_type to_node_type(node_attr_t bits) noexcept { + switch (bits & node_attr_mask::value) { + case seq_bit: + return node_type::SEQUENCE; + case map_bit: + return node_type::MAPPING; + case null_bit: + return node_type::NULL_OBJECT; + case bool_bit: + return node_type::BOOLEAN; + case int_bit: + return node_type::INTEGER; + case float_bit: + return node_type::FLOAT; + case string_bit: + return node_type::STRING; + default: // LCOV_EXCL_LINE + return node_type::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + +/// @brief Get an anchor offset used to reference an anchor node from the given attribute bits. +/// @param attrs node attribute bits +/// @return An anchor offset value. +inline uint32_t get_anchor_offset(node_attr_t attrs) noexcept { + return (attrs & node_attr_mask::anchor_offset) >> 26; +} + +/// @brief Set an anchor offset value to the appropriate bits. +/// @param offset An anchor offset value. +/// @param attrs node attribute bit set into which the offset value is written. +inline void set_anchor_offset(uint32_t offset, node_attr_t& attrs) noexcept { + attrs &= ~node_attr_mask::anchor_offset; + attrs |= (offset & 0x3Fu) << 26; +} + +} // namespace node_attr_bits + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_NODE_ATTRS_HPP_ */ diff --git a/include/fkYAML/detail/node_property.hpp b/include/fkYAML/detail/node_property.hpp index 2b62df0d..5a357a07 100644 --- a/include/fkYAML/detail/node_property.hpp +++ b/include/fkYAML/detail/node_property.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -17,21 +17,11 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN -enum class anchor_status_t { - NONE, - ANCHOR, - ALIAS, -}; - struct node_property { /// The tag name property. std::string tag {}; - /// The status regarding node anchoring/aliasing. - anchor_status_t anchor_status {anchor_status_t::NONE}; /// The anchor name property. std::string anchor {}; - /// The offset index value used to reference the anchor node implementation. - uint32_t anchor_offset {0}; }; FK_YAML_DETAIL_NAMESPACE_END diff --git a/include/fkYAML/detail/node_ref_storage.hpp b/include/fkYAML/detail/node_ref_storage.hpp index 552277ad..daaa1c9a 100644 --- a/include/fkYAML/detail/node_ref_storage.hpp +++ b/include/fkYAML/detail/node_ref_storage.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/output/serializer.hpp b/include/fkYAML/detail/output/serializer.hpp index 33ad1cd7..aab24303 100644 --- a/include/fkYAML/detail/output/serializer.hpp +++ b/include/fkYAML/detail/output/serializer.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -14,16 +14,16 @@ #include #include #include +#include #include #include #include -#include -#include +#include #include -#include -#include #include +#include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -90,10 +90,10 @@ class basic_serializer { if (p_meta->is_version_specified) { str += "%YAML "; switch (p_meta->version) { - case yaml_version_t::VER_1_1: + case yaml_version_type::VERSION_1_1: str += "1.1\n"; break; - case yaml_version_t::VER_1_2: + case yaml_version_type::VERSION_1_2: str += "1.2\n"; break; } @@ -137,8 +137,8 @@ class basic_serializer { /// @param cur_indent The current indent width /// @param str A string to hold serialization result. void serialize_node(const BasicNodeType& node, const uint32_t cur_indent, std::string& str) { - switch (node.type()) { - case node_t::SEQUENCE: + switch (node.get_type()) { + case node_type::SEQUENCE: for (const auto& seq_item : node) { insert_indentation(cur_indent, str); str += "-"; @@ -164,7 +164,7 @@ class basic_serializer { } } break; - case node_t::MAPPING: + case node_type::MAPPING: for (auto itr = node.begin(); itr != node.end(); ++itr) { insert_indentation(cur_indent, str); @@ -215,23 +215,23 @@ class basic_serializer { } } break; - case node_t::NULL_OBJECT: + case node_type::NULL_OBJECT: to_string(nullptr, m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::BOOLEAN: + case node_type::BOOLEAN: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::INTEGER: + case node_type::INTEGER: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::FLOAT_NUMBER: + case node_type::FLOAT: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::STRING: { + case node_type::STRING: { bool is_escaped = false; typename BasicNodeType::string_type str_val = get_string_node_value(node, is_escaped); @@ -244,11 +244,13 @@ class basic_serializer { break; } - auto adapter = input_adapter(str_val); - lexical_analyzer lexer(std::move(adapter)); - lexical_token_t token_type = lexer.get_next_token(); + // The next line is intentionally excluded from the LCOV coverage target since the next line is somehow + // misrecognized as it has a binary branch. Possibly begin() or end() has some conditional branch(es) + // internally. Confirmed with LCOV 1.14 on Ubuntu22.04. + node_type type_if_plain = + scalar_scanner::scan(str_val.c_str(), str_val.c_str() + str_val.size()); // LCOV_EXCL_LINE - if (token_type != lexical_token_t::STRING_VALUE) { + if (type_if_plain != node_type::STRING) { // Surround a string value with double quotes to keep semantic equality. // Without them, serialized values will become non-string. (e.g., "1" -> 1) str += '\"'; @@ -343,7 +345,7 @@ class basic_serializer { using string_type = typename BasicNodeType::string_type; const string_type& s = node.template get_value_ref(); - return yaml_escaper::escape(s.begin(), s.end(), is_escaped); + return yaml_escaper::escape(s.c_str(), s.c_str() + s.size(), is_escaped); } // LCOV_EXCL_LINE private: diff --git a/include/fkYAML/detail/str_view.hpp b/include/fkYAML/detail/str_view.hpp new file mode 100644 index 00000000..e6ee210a --- /dev/null +++ b/include/fkYAML/detail/str_view.hpp @@ -0,0 +1,963 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_STR_VIEW_HPP_ +#define FK_YAML_DETAIL_STR_VIEW_HPP_ + +#include +#include + +#include +#include +#include +#include + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Non owning view into constant character sequence. +/// @note +/// This class is a minimal implementation of std::basic_string_view which has been available since C++17 +/// but pretty useful and efficient for referencing/investigating character sequences. +/// @warning +/// This class intentionally omits a lot of value checks to improve efficiency. Necessary checks should be +/// made before calling this class' APIs for safety. +/// @tparam CharT Character type +/// @tparam Traits Character traits type which defaults to std::char_traits. +template > +class basic_str_view { + static_assert(!std::is_array::value, "CharT must not be an array type."); + static_assert( + std::is_trivial::value && std::is_standard_layout::value, + "CharT must be a trivial, standard layout type."); + static_assert( + std::is_same::value, "CharT & Traits::char_type must be the same type."); + +public: + /// Character traits type. + using traits_type = Traits; + /// Character type. + using value_type = CharT; + /// Pointer type to a character. + using pointer = value_type*; + /// Constant pointer type to a character. + using const_pointer = const value_type*; + /// Reference type to a character. + using reference = value_type&; + /// Constant reference type to a character. + using const_reference = const value_type&; + /// Constant iterator type to a character. + using const_iterator = const value_type*; + /// Iterator type to a character. + /// (Always constant since this class isn't meant to provide any mutating features.) + using iterator = const_iterator; + /// Constant reverse iterator type to a character. + using const_reverse_iterator = std::reverse_iterator; + /// Reverse iterator type to a character. + /// (Always constant since this class isn't meant to provide any mutating features.) + using reverse_iterator = const_reverse_iterator; + /// Size type for character sequence sizes. + using size_type = std::size_t; + /// Difference type for distances between characters. + using difference_type = std::ptrdiff_t; + + /// Invalid position value. + static constexpr size_type npos = static_cast(-1); + + /// Constructs a basic_str_view object. + basic_str_view() noexcept = default; + + /// Destroys a basic_str_view object. + ~basic_str_view() noexcept = default; + + /// @brief Copy constructs a basic_str_view object. + /// @param _ A basic_str_view object to copy from. + basic_str_view(const basic_str_view&) noexcept = default; + + /// @brief Move constructs a basic_str_view object. + /// @param _ A basic_str_view object to move from. + basic_str_view(basic_str_view&&) noexcept = default; + + /// @brief Constructs a basic_str_view object from a pointer to a character sequence. + /// @param p_str A pointer to a character sequence. (Must be null-terminated, or an undefined behavior.) + basic_str_view(const value_type* p_str) noexcept + : m_len(traits_type::length(p_str)), + mp_str(p_str) { + } + + /// @brief Construction from a null pointer is forbidden. + basic_str_view(std::nullptr_t) = delete; + + /// @brief Constructs a basic_str_view object from a poineter to a character sequence and its size. + /// @param p_str A pointer to a character sequence. (May or may not be null-terminated.) + /// @param len The length of a character sequence. + basic_str_view(const value_type* p_str, size_type len) noexcept + : m_len(len), + mp_str(p_str) { + } + + /// @brief Constructs a basic_str_view object from compatible begin/end iterators + /// @tparam ItrType Iterator type to a character. + /// @param first The iterator to the first element of a character sequence. + /// @param last The iterator to the past-the-end of a character sequence. + template < + typename ItrType, + enable_if_t< + conjunction< + is_iterator_of, + std::is_base_of< + std::random_access_iterator_tag, typename std::iterator_traits::iterator_category>>::value, + int> = 0> + basic_str_view(ItrType first, ItrType last) noexcept + : m_len(last - first), + mp_str(m_len > 0 ? &*first : nullptr) { + } + + /// @brief Constructs a basic_str_view object from a compatible std::basic_string object. + /// @param str A compatible character sequence container. + basic_str_view(const std::basic_string& str) noexcept + : m_len(str.length()), + mp_str(str.data()) { + } + + /// @brief Copy assignment operator for this basic_str_view class. + /// @param _ A basic_str_view object to copy from. + /// @return Reference to this basic_str_view object. + basic_str_view& operator=(const basic_str_view&) noexcept = default; + + /// @brief Move assignment operator for this basic_str_view class. + /// @param _ A basic_str_view object to move from. + /// @return Reference to this basic_str_view object. + basic_str_view& operator=(basic_str_view&&) noexcept = default; + + /// @brief Get the iterator to the first element. (Always constant) + /// @return The iterator to the first element. + const_iterator begin() const noexcept { + return mp_str; + } + + /// @brief Get the iterator to the past-the-end element. (Always constant) + /// @return The iterator to the past-the-end element. + const_iterator end() const noexcept { + return mp_str + m_len; + } + + /// @brief Get the iterator to the first element. (Always constant) + /// @return The iterator to the first element. + const_iterator cbegin() const noexcept { + return mp_str; + } + + /// @brief Get the iterator to the past-the-end element. (Always constant) + /// @return The iterator to the past-the-end element. + const_iterator cend() const noexcept { + return mp_str + m_len; + } + + /// @brief Get the iterator to the first element in the reverse order. (Always constant) + /// @return The iterator to the first element in the reverse order. + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant) + /// @return The iterator to the past-the-end element in the reverse order. + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + /// @brief Get the iterator to the first element in the reverse order. (Always constant) + /// @return The iterator to the first element in the reverse order. + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant) + /// @return The iterator to the past-the-end element in the reverse order. + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(begin()); + } + + /// @brief Get the size of the referenced character sequence. + /// @return The size of the referenced character sequence. + size_type size() const noexcept { + return m_len; + } + + /// @brief Get the size of the referenced character sequence. + /// @return The size of the referenced character sequence. + size_type length() const noexcept { + return m_len; + } + + /// @brief Get the maximum number of the character sequence size. + /// @return The maximum number of the character sequence size. + constexpr size_type max_size() const noexcept { + return static_cast(std::numeric_limits::max()); + } + + /// @brief Checks if the referenced character sequence is empty. + /// @return true if empty, false otherwise. + bool empty() const noexcept { + return m_len == 0; + } + + /// @brief Get the element at the given position. + /// @param pos The position of the target element. + /// @return The element at the given position. + const_reference operator[](size_type pos) const noexcept { + return *(mp_str + pos); + } + + /// @brief Get the element at the given position with bounds checks. + /// @warning Throws an fkyaml::out_of_range exception if the position exceeds the character sequence size. + /// @param pos The position of the target element. + /// @return The element at the given position. + const_reference at(size_type pos) const { + if FK_YAML_UNLIKELY (pos >= m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + return *(mp_str + pos); + } + + /// @brief Get the first element. + /// @return The first element. + const_reference front() const noexcept { + return *mp_str; + } + + /// @brief Get the last element. + /// @return The last element. + const_reference back() const { + return *(mp_str + m_len - 1); + } + + /// @brief Get the pointer to the raw data of referenced character sequence. + /// @return The pointer to the raw data of referenced character sequence. + const_pointer data() const noexcept { + return mp_str; + } + + /// @brief Moves the beginning position by `n` elements. + /// @param n The number of elements by which to move the beginning position. + void remove_prefix(size_type n) noexcept { + mp_str += n; + m_len -= n; + } + + /// @brief Shrinks the referenced character sequence from the last by `n` elements. + /// @param n The number of elements by which to shrink the sequence from the last. + void remove_suffix(size_type n) noexcept { + m_len -= n; + } + + /// @brief Swaps data with the given basic_str_view object. + /// @param other A basic_str_view object to swap data with. + void swap(basic_str_view& other) noexcept { + auto tmp = *this; + *this = other; + other = tmp; + } + + /// @brief Copys the referenced character sequence values from `pos` by `n` size. + /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the lenth. + /// @param p_str The pointer to a character sequence buffer for output. + /// @param n The number of elements to write into `p_str`. + /// @param pos The offset of the beginning position to copy values. + /// @return The number of elements to be written into `p_str`. + size_type copy(CharT* p_str, size_type n, size_type pos = 0) const { + if FK_YAML_UNLIKELY (pos > m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + const size_type rlen = std::min(n, m_len - pos); + traits_type::copy(p_str, mp_str + pos, rlen); + return rlen; + } + + /// @brief Constructs a sub basic_str_view object from `pos` by `n` size. + /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the lenth. + /// @param pos The offset of the beginning position. + /// @param n The number of elements to the end of a new sub basic_str_view object. + /// @return A newly created sub basic_str_view object. + basic_str_view substr(size_type pos = 0, size_type n = npos) const { + if FK_YAML_UNLIKELY (pos > m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + const size_type rlen = std::min(n, m_len - pos); + return basic_str_view(mp_str + pos, rlen); + } + + /// @brief Compares the referenced character sequence values with the given basic_str_view object. + /// @param sv The basic_str_view object to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(basic_str_view sv) const noexcept { + const size_type rlen = std::min(m_len, sv.m_len); + int ret = traits_type::compare(mp_str, sv.mp_str, rlen); + + if (ret == 0) { + using int_limits = std::numeric_limits; + difference_type diff = + m_len > sv.m_len ? m_len - sv.m_len : difference_type(-1) * difference_type(sv.m_len - m_len); + + if (diff > int_limits::max()) { + ret = int_limits::max(); + } + else if (diff < int_limits::min()) { + ret = int_limits::min(); + } + else { + ret = static_cast(diff); + } + } + + return ret; + } + + /// @brief Compares the referenced character sequence values from `pos1` by `n1` characters with `sv`. + /// @param pos1 The offset of the beginning element. + /// @param n1 The length of character sequence used for comparison. + /// @param sv A basic_str_view object to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, basic_str_view sv) const { + return substr(pos1, n1).compare(sv); + } + + /// @brief Compares the referenced character sequence value from `pos1` by `n1` characters with `sv` from `pos2` by + /// `n2` characters. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used for comparison. + /// @param sv A basic_str_view object to compare with. + /// @param pos2 The offset of the beginning element in `sv`. + /// @param n2 The length of `sv` used for comparison. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, basic_str_view sv, size_type pos2, size_type n2) const { + return substr(pos1, n1).compare(sv.substr(pos2, n2)); + } + + /// @brief Compares the referenced character sequence with `s` character sequence. + /// @param s The pointer to a character sequence to compare with. + /// @return The lexicolographical comparison result. The values are same as std::strncmp(). + int compare(const CharT* s) const { + return compare(basic_str_view(s)); + } + + /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used fo comparison. + /// @param s The pointer to a character sequence to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, const CharT* s) const { + return substr(pos1, n1).compare(basic_str_view(s)); + } + + /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence by + /// `n2` characters. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used fo comparison. + /// @param s The pointer to a character sequence to compare with. + /// @param n2 The length of `s` used fo comparison. + /// @return + int compare(size_type pos1, size_type n1, const CharT* s, size_type n2) const { + return substr(pos1, n1).compare(basic_str_view(s, n2)); + } + + /// @brief Checks if this character sequence starts with `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence starts with `sv` characters, false otherwise. + bool starts_with(basic_str_view sv) const noexcept { + return substr(0, sv.size()) == sv; + } + + /// @brief Checks if this character sequence starts with `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence starts with `c` character, false otherwise. + bool starts_with(CharT c) const noexcept { + return !empty() && traits_type::eq(front(), c); + } + + /// @brief Checks if this character sequence starts with `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence starts with `s` characters, false otherwise. + bool starts_with(const CharT* s) const noexcept { + return starts_with(basic_str_view(s)); + } + + /// @brief Checks if this character sequence ends with `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence ends with `sv` characters, false otherwise. + bool ends_with(basic_str_view sv) const noexcept { + const size_type size = m_len; + const size_type sv_size = sv.size(); + return size >= sv_size && traits_type::compare(end() - sv_size, sv.data(), sv_size) == 0; + } + + /// @brief Checks if this character sequence ends with `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence ends with `c` character, false otherwise. + bool ends_with(CharT c) const noexcept { + return !empty() && traits_type::eq(back(), c); + } + + /// @brief Checks if this character sequence ends with `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence ends with `s` characters, false otherwise. + bool ends_with(const CharT* s) const noexcept { + return ends_with(basic_str_view(s)); + } + + /// @brief Checks if this character sequence contains `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence contains `sv` characters, false otherwise. + bool contains(basic_str_view sv) const noexcept { + return find(sv) != npos; + } + + /// @brief Checks if this character sequence contains `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence contains `c` character, false otherwise. + bool contains(CharT c) const noexcept { + return find(c) != npos; + } + + /// @brief Checks if this character sequence contains `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence contains `s` characters, false otherwise. + bool contains(const CharT* s) const noexcept { + return find(s) != npos; + } + + /// @brief Finds the beginning position of `sv` characters in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find(basic_str_view sv, size_type pos = 0) const noexcept { + return find(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the beginning position of `c` character in this referenced character sequence. + /// @param sv The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find(CharT c, size_type pos = 0) const noexcept { + size_type ret = npos; + + if FK_YAML_LIKELY (pos < m_len) { + const size_type n = m_len - pos; + const CharT* p_found = traits_type::find(mp_str + pos, n, c); + if (p_found) { + ret = p_found - mp_str; + } + } + + return ret; + } + + /// @brief Finds the beginning position of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n == 0) { + return pos <= m_len ? pos : npos; + } + + if FK_YAML_UNLIKELY (pos >= m_len) { + return npos; + } + + CharT s0 = s[0]; + const CharT* p_first = mp_str + pos; + const CharT* p_last = mp_str + m_len; + size_type len = m_len - pos; + + while (len >= n) { + // find the first occurence of s0 + p_first = traits_type::find(p_first, len - n + 1, s0); + if (!p_first) { + return npos; + } + + // compare the full strings from the first occurence of s0 + if (traits_type::compare(p_first, s, n) == 0) { + return p_first - mp_str; + } + + len = p_last - (++p_first); + } + + return npos; + } + + /// @brief Finds the beginning position of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find(const CharT* s, size_type pos = 0) const noexcept { + return find(basic_str_view(s), pos); + } + + /// @brief Retrospectively finds the beginning position of `sv` characters in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type rfind(basic_str_view sv, size_type pos = npos) const noexcept { + return rfind(sv.mp_str, pos, sv.m_len); + } + + /// @brief Retrospectively finds the beginning position of `c` character in this referenced character sequence. + /// @param sv The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type rfind(CharT c, size_type pos = npos) const noexcept { + if FK_YAML_UNLIKELY (m_len == 0) { + return npos; + } + + size_type idx = pos; + if (pos >= m_len) { + idx = m_len - 1; + } + + do { + if (traits_type::eq(mp_str[idx], c)) { + return idx; + } + } while (idx > 0 && --idx < m_len); + + return npos; + } + + /// @brief Retrospectively finds the beginning position of `s` character sequence by `n` characters in this + /// referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type rfind(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_LIKELY (n <= m_len) { + pos = std::min(m_len - n, pos) + 1; + + do { + if (traits_type::compare(mp_str + --pos, s, n) == 0) { + return pos; + } + } while (pos > 0); + } + + return npos; + } + + /// @brief Retrospectively finds the beginning position of `s` character sequence in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type rfind(const CharT* s, size_type pos = npos) const noexcept { + return rfind(basic_str_view(s), pos); + } + + /// @brief Finds the first occurence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find_first_of(basic_str_view sv, size_type pos = 0) const noexcept { + return find_first_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the first occurence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find_first_of(CharT c, size_type pos = 0) const noexcept { + return find(c, pos); + } + + /// @brief Finds the first occurence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_first_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n == 0) { + return npos; + } + + for (size_type idx = pos; idx < m_len; ++idx) { + const CharT* p_found = traits_type::find(s, n, mp_str[idx]); + if (p_found) { + return idx; + } + } + + return npos; + } + + /// @brief Finds the first occurence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_first_of(const CharT* s, size_type pos = 0) const noexcept { + return find_first_of(basic_str_view(s), pos); + } + + /// @brief Finds the last occurence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find_last_of(basic_str_view sv, size_type pos = npos) const noexcept { + return find_last_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the last occurence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find_last_of(CharT c, size_type pos = npos) const noexcept { + return rfind(c, pos); + } + + /// @brief Finds the last occurence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_last_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_LIKELY (n <= m_len) { + pos = std::min(m_len - n - 1, pos); + + do { + const CharT* p_found = traits_type::find(s, n, mp_str[pos]); + if (p_found) { + return pos; + } + } while (pos-- != 0); + } + + return npos; + } + + /// @brief Finds the last occurence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_last_of(const CharT* s, size_type pos = npos) const noexcept { + return find_last_of(basic_str_view(s), pos); + } + + /// @brief Finds the first absence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `sv` characters, `npos` otherwise. + size_type find_first_not_of(basic_str_view sv, size_type pos = 0) const noexcept { + return find_first_not_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the first absence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `c` character, `npos` otherwise. + size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept { + for (; pos < m_len; ++pos) { + if (!traits_type::eq(mp_str[pos], c)) { + return pos; + } + } + + return npos; + } + + /// @brief Finds the first absence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const noexcept { + for (; pos < m_len; ++pos) { + const CharT* p_found = traits_type::find(s, n, mp_str[pos]); + if (!p_found) { + return pos; + } + } + + return npos; + } + + /// @brief Finds the first absence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_first_not_of(const CharT* s, size_type pos = 0) const noexcept { + return find_first_not_of(basic_str_view(s), pos); + } + + /// @brief Finds the last absence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `sv` characters, `npos` otherwise. + size_type find_last_not_of(basic_str_view sv, size_type pos = npos) const noexcept { + return find_last_not_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the last absence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `c` character, `npos` otherwise. + size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept { + if FK_YAML_LIKELY (m_len > 0) { + pos = std::min(m_len, pos); + + do { + if (!traits_type::eq(mp_str[--pos], c)) { + return pos; + } + } while (pos > 0); + } + + return npos; + } + + /// @brief Finds the last absence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n <= m_len) { + pos = std::min(m_len - n, pos) + 1; + + do { + const CharT* p_found = traits_type::find(s, n, mp_str[--pos]); + if (!p_found) { + return pos; + } + } while (pos > 0); + } + + return npos; + } + + /// @brief Finds the last absence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_last_not_of(const CharT* s, size_type pos = npos) const noexcept { + return find_last_not_of(basic_str_view(s), pos); + } + +private: + size_type m_len {0}; + const value_type* mp_str {nullptr}; +}; + +// Prior to C++17, a static constexpr class member needs an out-of-class definition. +#ifndef FK_YAML_HAS_CXX_17 + +template +constexpr typename basic_str_view::size_type basic_str_view::npos; + +#endif // !defined(FK_YAML_HAS_CXX_17) + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, basic_str_view rhs) noexcept { + // Comparing the lengths first will omit unnecessary value comparison in compare(). + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; +} + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_string object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, const std::basic_string& rhs) noexcept { + return lhs == basic_str_view(rhs); +} + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_string object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(const std::basic_string& lhs, basic_str_view rhs) noexcept { + return basic_str_view(lhs) == rhs; +} + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A character array to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, const CharT (&rhs)[N]) noexcept { + // assume `rhs` is null terminated + return lhs == basic_str_view(rhs, N - 1); +} + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param rhs A character array for comparison. +/// @param lhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(const CharT (&lhs)[N], basic_str_view rhs) noexcept { + // assume `lhs` is null terminated + return basic_str_view(lhs, N - 1) == rhs; +} + +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, basic_str_view rhs) noexcept { + return !(lhs == rhs); +} + +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_string object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, const std::basic_string& rhs) noexcept { + return !(lhs == basic_str_view(rhs)); +} + +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_string object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(const std::basic_string& lhs, basic_str_view rhs) noexcept { + return !(basic_str_view(lhs) == rhs); +} + +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A character array to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, const CharT (&rhs)[N]) noexcept { + // assume `rhs` is null terminated. + return !(lhs == basic_str_view(rhs, N - 1)); +} + +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param rhs A character array for comparison. +/// @param lhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(const CharT (&lhs)[N], basic_str_view rhs) noexcept { + // assume `lhs` is null terminate + return !(basic_str_view(lhs, N - 1) == rhs); +} + +/// @brief An less-than operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is less than `rhs`, false otherwise. +template +inline bool operator<(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +/// @brief An less-than-or-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is less than or equal to `rhs`, false otherwise. +template +inline bool operator<=(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +/// @brief An greater-than operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is greater than `rhs`, false otherwise. +template +inline bool operator>(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +/// @brief An greater-than-or-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is greater than or equal to `rhs`, false otherwise. +template +inline bool operator>=(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +/// @brief Insertion operator of the basic_str_view class. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +/// @param os An output stream object. +/// @param sv A basic_str_view object. +/// @return Reference to the output stream object `os`. +template +inline std::basic_ostream& operator<<( + std::basic_ostream& os, basic_str_view sv) { + return os.write(sv.data(), static_cast(sv.size())); +} + +/// @brief view into `char` sequence. +using str_view = basic_str_view; + +#if FK_YAML_HAS_CHAR8_T +/// @brief view into `char8_t` sequence. +using u8str_view = basic_str_view; +#endif + +/// @brief view into `char16_t` sequence. +using u16str_view = basic_str_view; + +/// @brief view into `char32_t` sequence. +using u32str_view = basic_str_view; + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_STR_VIEW_HPP_ */ diff --git a/include/fkYAML/detail/string_formatter.hpp b/include/fkYAML/detail/string_formatter.hpp index f13bd6eb..ff3ff0dd 100644 --- a/include/fkYAML/detail/string_formatter.hpp +++ b/include/fkYAML/detail/string_formatter.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/detail/types/lexical_token_t.hpp b/include/fkYAML/detail/types/lexical_token_t.hpp index 3e23c37f..9e4469d6 100644 --- a/include/fkYAML/detail/types/lexical_token_t.hpp +++ b/include/fkYAML/detail/types/lexical_token_t.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -32,11 +32,10 @@ enum class lexical_token_t { SEQUENCE_FLOW_END, //!< the character for sequence flow end `]` MAPPING_FLOW_BEGIN, //!< the character for mapping begin `{` MAPPING_FLOW_END, //!< the character for mapping end `}` - NULL_VALUE, //!< a null value found. use get_null() to get a value. - BOOLEAN_VALUE, //!< a boolean value found. use get_boolean() to get a value. - INTEGER_VALUE, //!< an integer value found. use get_integer() to get a value. - FLOAT_NUMBER_VALUE, //!< a float number value found. use get_float_number() to get a value. - STRING_VALUE, //!< the character for string begin `"` or any character except the above ones + PLAIN_SCALAR, //!< plain (unquoted) scalars + SINGLE_QUOTED_SCALAR, //!< single-quoted scalars + DOUBLE_QUOTED_SCALAR, //!< double-quoted scalars + BLOCK_SCALAR, //!< block style scalars END_OF_DIRECTIVES, //!< the end of declaration of directives specified by `---`. END_OF_DOCUMENT, //!< the end of a YAML document specified by `...`. }; diff --git a/include/fkYAML/detail/types/node_t.hpp b/include/fkYAML/detail/types/node_t.hpp index 588a1fda..62d8c210 100644 --- a/include/fkYAML/detail/types/node_t.hpp +++ b/include/fkYAML/detail/types/node_t.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -15,6 +15,7 @@ #include #include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -50,6 +51,48 @@ inline const char* to_string(node_t t) noexcept { } } +inline node_t convert_from_node_type(node_type t) { + switch (t) { + case node_type::SEQUENCE: + return node_t::SEQUENCE; + case node_type::MAPPING: + return node_t::MAPPING; + case node_type::NULL_OBJECT: + return node_t::NULL_OBJECT; + case node_type::BOOLEAN: + return node_t::BOOLEAN; + case node_type::INTEGER: + return node_t::INTEGER; + case node_type::FLOAT: + return node_t::FLOAT_NUMBER; + case node_type::STRING: + return node_t::STRING; + default: // LCOV_EXCL_LINE + return node_t::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + +inline node_type convert_to_node_type(node_t t) { + switch (t) { + case node_t::SEQUENCE: + return node_type::SEQUENCE; + case node_t::MAPPING: + return node_type::MAPPING; + case node_t::NULL_OBJECT: + return node_type::NULL_OBJECT; + case node_t::BOOLEAN: + return node_type::BOOLEAN; + case node_t::INTEGER: + return node_type::INTEGER; + case node_t::FLOAT_NUMBER: + return node_type::FLOAT; + case node_t::STRING: + return node_type::STRING; + default: // LCOV_EXCL_LINE + return node_type::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_TYPES_NODE_T_HPP_ */ diff --git a/include/fkYAML/detail/types/yaml_version_t.hpp b/include/fkYAML/detail/types/yaml_version_t.hpp index 46ed388c..dfb505eb 100644 --- a/include/fkYAML/detail/types/yaml_version_t.hpp +++ b/include/fkYAML/detail/types/yaml_version_t.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -14,6 +14,7 @@ #include #include +#include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -23,6 +24,28 @@ enum class yaml_version_t : std::uint32_t { VER_1_2, //!< YAML version 1.2 }; +inline yaml_version_t convert_from_yaml_version_type(yaml_version_type t) noexcept { + switch (t) { + case yaml_version_type::VERSION_1_1: + return yaml_version_t::VER_1_1; + case yaml_version_type::VERSION_1_2: + return yaml_version_t::VER_1_2; + default: // LCOV_EXCL_LINE + return yaml_version_t::VER_1_2; // LCOV_EXCL_LINE + } +} + +inline yaml_version_type convert_to_yaml_version_type(yaml_version_t t) noexcept { + switch (t) { + case yaml_version_t::VER_1_1: + return yaml_version_type::VERSION_1_1; + case yaml_version_t::VER_1_2: + return yaml_version_type::VERSION_1_2; + default: // LCOV_EXCL_LINE + return yaml_version_type::VERSION_1_2; // LCOV_EXCL_LINE + } +} + FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ */ diff --git a/include/fkYAML/exception.hpp b/include/fkYAML/exception.hpp index b664469c..4e358a96 100644 --- a/include/fkYAML/exception.hpp +++ b/include/fkYAML/exception.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -130,17 +130,26 @@ class type_error : public exception { /// @brief Construct a new type_error object with an error message and a node type. /// @param[in] msg An error message. /// @param[in] type The type of a source node value. - explicit type_error(const char* msg, detail::node_t type) noexcept + explicit type_error(const char* msg, node_type type) noexcept : exception(generate_error_message(msg, type).c_str()) { } + /// @brief Construct a new type_error object with an error message and a node type. + /// @deprecated Use type_error(const char*, node_type) constructor. (since 0.3.12). + /// @param[in] msg An error message. + /// @param[in] type The type of a source node value. + FK_YAML_DEPRECATED("Since 0.3.12; Use explicit type_error(const char*, node_type)") + explicit type_error(const char* msg, detail::node_t type) noexcept + : type_error(msg, detail::convert_to_node_type(type)) { + } + private: /// @brief Generate an error message from given parameters. /// @param msg An error message. /// @param type The type of a source node value. /// @return A generated error message. - std::string generate_error_message(const char* msg, detail::node_t type) const noexcept { - return detail::format("type_error: %s type=%s", msg, detail::to_string(type)); + std::string generate_error_message(const char* msg, node_type type) const noexcept { + return detail::format("type_error: %s type=%s", msg, to_string(type)); } }; diff --git a/include/fkYAML/node.hpp b/include/fkYAML/node.hpp index bd1984fb..308f6415 100644 --- a/include/fkYAML/node.hpp +++ b/include/fkYAML/node.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -30,11 +30,14 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include #include @@ -88,15 +91,17 @@ class basic_node { using value_converter_type = ConverterType; /// @brief Definition of node value types. + /// @deprecated Use fkyaml::node_type enum class. (since 0.3.12) /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/node_t/ using node_t = detail::node_t; /// @brief Definition of YAML version types. + /// @deprecated Use fkyaml::yaml_version_type enum class. (since 0.3.12) /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/yaml_version_t/ using yaml_version_t = detail::yaml_version_t; private: - template + template friend struct fkyaml::detail::external_node_constructor; template @@ -123,48 +128,50 @@ class basic_node { /// @brief Constructs a new basic_node value object with a node type. The default value for the specified /// type will be assigned. /// @param[in] type A node type. - explicit node_value(node_t type) { - switch (type) { - case node_t::SEQUENCE: + explicit node_value(detail::node_attr_t value_type_bit) { + switch (value_type_bit) { + case detail::node_attr_bits::seq_bit: p_sequence = create_object(); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: p_mapping = create_object(); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: p_mapping = nullptr; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: boolean = static_cast(false); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: integer = static_cast(0); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: float_val = static_cast(0.0); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: p_string = create_object(); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } /// @brief Destroys the existing Node value. This process is recursive if the specified node type is for /// containers. /// @param[in] type A Node type to determine the value to be destroyed. - void destroy(node_t type) { - switch (type) { - case node_t::SEQUENCE: + void destroy(detail::node_attr_t value_type_bit) { + switch (value_type_bit) { + case detail::node_attr_bits::seq_bit: p_sequence->clear(); destroy_object(p_sequence); p_sequence = nullptr; break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: p_mapping->clear(); destroy_object(p_mapping); p_mapping = nullptr; break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: destroy_object(p_string); p_string = nullptr; break; @@ -231,41 +238,48 @@ class basic_node { /// @brief Constructs a new basic_node object with a specified type. /// @param[in] type A YAML node type. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ + FK_YAML_DEPRECATED("Since 0.3.12; Use explicit basic_node(const node_type)") explicit basic_node(const node_t type) - : m_node_type(type), - m_node_value(type) { + : basic_node(detail::convert_to_node_type(type)) { + } + + explicit basic_node(const node_type type) + : m_attrs(detail::node_attr_bits::from_node_type(type)), + m_node_value(m_attrs & detail::node_attr_mask::value) { } /// @brief Copy constructor of the basic_node class. /// @param[in] rhs A basic_node object to be copied with. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ basic_node(const basic_node& rhs) - : m_node_type(rhs.m_node_type), + : m_attrs(rhs.m_attrs), mp_meta(rhs.mp_meta), m_prop(rhs.m_prop) { - if (!has_anchor_name()) { - switch (m_node_type) { - case node_t::SEQUENCE: + if FK_YAML_LIKELY (!has_anchor_name()) { + switch (m_attrs & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: m_node_value.p_sequence = create_object(*(rhs.m_node_value.p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: m_node_value.p_mapping = create_object(*(rhs.m_node_value.p_mapping)); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: m_node_value.p_mapping = nullptr; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: m_node_value.boolean = rhs.m_node_value.boolean; break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: m_node_value.integer = rhs.m_node_value.integer; break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: m_node_value.float_val = rhs.m_node_value.float_val; break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: m_node_value.p_string = create_object(*(rhs.m_node_value.p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } } @@ -274,48 +288,49 @@ class basic_node { /// @param[in] rhs A basic_node object to be moved from. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ basic_node(basic_node&& rhs) noexcept - : m_node_type(rhs.m_node_type), + : m_attrs(rhs.m_attrs), mp_meta(std::move(rhs.mp_meta)), m_prop(std::move(rhs.m_prop)) { - if (!has_anchor_name()) { - switch (m_node_type) { - case node_t::SEQUENCE: + if FK_YAML_LIKELY (!has_anchor_name()) { + switch (m_attrs & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: FK_YAML_ASSERT(rhs.m_node_value.p_sequence != nullptr); m_node_value.p_sequence = rhs.m_node_value.p_sequence; rhs.m_node_value.p_sequence = nullptr; break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: FK_YAML_ASSERT(rhs.m_node_value.p_mapping != nullptr); m_node_value.p_mapping = rhs.m_node_value.p_mapping; rhs.m_node_value.p_mapping = nullptr; break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: FK_YAML_ASSERT(rhs.m_node_value.p_mapping == nullptr); m_node_value.p_mapping = rhs.m_node_value.p_mapping; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: m_node_value.boolean = rhs.m_node_value.boolean; rhs.m_node_value.boolean = static_cast(false); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: m_node_value.integer = rhs.m_node_value.integer; rhs.m_node_value.integer = static_cast(0); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: m_node_value.float_val = rhs.m_node_value.float_val; rhs.m_node_value.float_val = static_cast(0.0); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: FK_YAML_ASSERT(rhs.m_node_value.p_string != nullptr); m_node_value.p_string = rhs.m_node_value.p_string; rhs.m_node_value.p_string = nullptr; break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } - rhs.m_node_type = node_t::NULL_OBJECT; + rhs.m_attrs = detail::node_attr_bits::default_bits; rhs.m_node_value.p_mapping = nullptr; - rhs.m_prop.anchor_status = detail::anchor_status_t::NONE; } /// @brief Construct a new basic_node object from a value of compatible types. @@ -356,7 +371,7 @@ class basic_node { }); if (is_mapping) { - m_node_type = node_t::MAPPING; + m_attrs = detail::node_attr_bits::map_bit; m_node_value.p_mapping = create_object(); for (auto& elem_ref : init) { @@ -366,7 +381,7 @@ class basic_node { } } else { - m_node_type = node_t::SEQUENCE; + m_attrs = detail::node_attr_bits::seq_bit; m_node_value.p_sequence = create_object(); m_node_value.p_sequence->reserve(std::distance(init.begin(), init.end())); for (auto& elem_ref : init) { @@ -379,24 +394,20 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/destructor/ ~basic_node() noexcept // NOLINT(bugprone-exception-escape) { - switch (m_prop.anchor_status) { - case detail::anchor_status_t::NONE: - if (m_node_type != node_t::NULL_OBJECT) { - m_node_value.destroy(m_node_type); + if (m_attrs & detail::node_attr_mask::anchoring) { + if (m_attrs & detail::node_attr_bits::anchor_bit) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + itr->second.m_node_value.destroy(itr->second.m_attrs & detail::node_attr_mask::value); + itr->second.m_attrs = detail::node_attr_bits::default_bits; + itr->second.mp_meta.reset(); } - break; - case detail::anchor_status_t::ANCHOR: { - auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); - itr->second.m_node_value.destroy(itr->second.m_node_type); - itr->second.m_node_type = node_t::NULL_OBJECT; - itr->second.mp_meta.reset(); - break; } - case detail::anchor_status_t::ALIAS: - break; + else if ((m_attrs & detail::node_attr_bits::null_bit) == 0) { + m_node_value.destroy(m_attrs & detail::node_attr_mask::value); } - m_node_type = node_t::NULL_OBJECT; + + m_attrs = detail::node_attr_bits::default_bits; mp_meta.reset(); } @@ -466,7 +477,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence() { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(); return node; } // LCOV_EXCL_LINE @@ -477,7 +488,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence(const sequence_type& seq) { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(seq); return node; } // LCOV_EXCL_LINE @@ -488,7 +499,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence(sequence_type&& seq) { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(std::move(seq)); return node; } // LCOV_EXCL_LINE @@ -498,7 +509,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping() { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(); return node; } // LCOV_EXCL_LINE @@ -509,7 +520,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping(const mapping_type& map) { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(map); return node; } // LCOV_EXCL_LINE @@ -520,7 +531,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping(mapping_type&& map) { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(std::move(map)); return node; } // LCOV_EXCL_LINE @@ -531,12 +542,15 @@ class basic_node { /// @return An alias YAML node created from the given anchor node. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/alias_of/ static basic_node alias_of(const basic_node& anchor_node) { - if (!anchor_node.has_anchor_name() || anchor_node.m_prop.anchor_status != detail::anchor_status_t::ANCHOR) { + using namespace detail::node_attr_bits; + + if FK_YAML_UNLIKELY (!anchor_node.has_anchor_name() || !(anchor_node.m_attrs & anchor_bit)) { throw fkyaml::exception("Cannot create an alias without anchor name."); } basic_node node = anchor_node; - node.m_prop.anchor_status = detail::anchor_status_t::ALIAS; + node.m_attrs &= ~detail::node_attr_mask::anchoring; + node.m_attrs |= alias_bit; return node; } // LCOV_EXCL_LINE @@ -571,16 +585,17 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> basic_node& operator[](KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } basic_node n = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!n.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](n.get_value()); @@ -602,16 +617,17 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> const basic_node& operator[](KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](node_key.get_value()); @@ -629,15 +645,16 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> basic_node& operator[](KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](key.template get_value()); @@ -655,15 +672,16 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> const basic_node& operator[](KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](key.template get_value()); @@ -678,40 +696,42 @@ class basic_node { /// @return true if both types and values are equal, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_eq/ bool operator==(const basic_node& rhs) const noexcept { - node_t this_type = type(); - const node_value* this_node_value_ptr = get_node_value_ptr(); - const node_value* other_node_value_ptr = rhs.get_node_value_ptr(); - - if (this_type != rhs.type()) { + detail::node_attr_t this_val_bit = get_node_attrs() & detail::node_attr_mask::value; + if (this_val_bit != (rhs.get_node_attrs() & detail::node_attr_mask::value)) { return false; } + const node_value* this_node_value_ptr = get_node_value_ptr(); + const node_value* other_node_value_ptr = rhs.get_node_value_ptr(); + bool ret = false; - switch (this_type) { - case node_t::SEQUENCE: + switch (this_val_bit) { + case detail::node_attr_bits::seq_bit: ret = (*(this_node_value_ptr->p_sequence) == *(other_node_value_ptr->p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: ret = (*(this_node_value_ptr->p_mapping) == *(other_node_value_ptr->p_mapping)); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: // Always true for comparisons between null nodes. ret = true; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: ret = (this_node_value_ptr->boolean == other_node_value_ptr->boolean); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: ret = (this_node_value_ptr->integer == other_node_value_ptr->integer); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: ret = (std::abs(this_node_value_ptr->float_val - other_node_value_ptr->float_val) < std::numeric_limits::epsilon()); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: ret = (*(this_node_value_ptr->p_string) == *(other_node_value_ptr->p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } return ret; @@ -734,14 +754,14 @@ class basic_node { return false; } - node_t this_type = type(); - node_t other_type = rhs.type(); + detail::node_attr_t this_val_bit = get_node_attrs() & detail::node_attr_mask::value; + detail::node_attr_t other_val_bit = rhs.get_node_attrs() & detail::node_attr_mask::value; - if (static_cast(this_type) < static_cast(other_type)) { + if (this_val_bit < other_val_bit) { return true; } - if (this_type != other_type) { + if (this_val_bit != other_val_bit) { return false; } @@ -749,29 +769,31 @@ class basic_node { const node_value* p_other_value = rhs.get_node_value_ptr(); bool ret = false; - switch (this_type) { - case node_t::SEQUENCE: + switch (this_val_bit) { + case detail::node_attr_bits::seq_bit: ret = (*(p_this_value->p_sequence) < *(p_other_value->p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: ret = (*(p_this_value->p_mapping) < *(p_other_value->p_mapping)); break; - case node_t::NULL_OBJECT: // LCOV_EXCL_LINE + case detail::node_attr_bits::null_bit: // LCOV_EXCL_LINE // Will not come here since null nodes are alyways the same. break; // LCOV_EXCL_LINE - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: // false < true ret = (!p_this_value->boolean && p_other_value->boolean); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: ret = (p_this_value->integer < p_other_value->integer); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: ret = (p_this_value->float_val < p_other_value->float_val); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: ret = (*(p_this_value->p_string) < *(p_other_value->p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } return ret; @@ -804,108 +826,114 @@ class basic_node { public: /// @brief Returns the type of the current basic_node value. /// @return The type of the YAML node value. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_type/ + node_type get_type() const noexcept { + detail::node_attr_t attrs = get_node_attrs(); + return detail::node_attr_bits::to_node_type(attrs); + } + + /// @brief Returns the type of the current basic_node value. + /// @deprecated Use get_type() function. (since 0.3.12) + /// @return The type of the YAML node value. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/type/ + FK_YAML_DEPRECATED("Since 0.3.12; Use get_type()") node_t type() const noexcept { - if (has_anchor_name()) { - auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); - return itr->second.m_node_type; - } - return m_node_type; + node_type tmp_type = get_type(); + return detail::convert_from_node_type(tmp_type); } /// @brief Tests whether the current basic_node value is of sequence type. /// @return true if the type is sequence, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_sequence/ bool is_sequence() const noexcept { - return type() == node_t::SEQUENCE; + return get_node_attrs() & detail::node_attr_bits::seq_bit; } /// @brief Tests whether the current basic_node value is of mapping type. /// @return true if the type is mapping, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_mapping/ bool is_mapping() const noexcept { - return type() == node_t::MAPPING; + return get_node_attrs() & detail::node_attr_bits::map_bit; } /// @brief Tests whether the current basic_node value is of null type. /// @return true if the type is null, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_null/ bool is_null() const noexcept { - return type() == node_t::NULL_OBJECT; + return get_node_attrs() & detail::node_attr_bits::null_bit; } /// @brief Tests whether the current basic_node value is of boolean type. /// @return true if the type is boolean, false otherwise /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_boolean/ bool is_boolean() const noexcept { - return type() == node_t::BOOLEAN; + return get_node_attrs() & detail::node_attr_bits::bool_bit; } /// @brief Tests whether the current basic_node value is of integer type. /// @return true if the type is integer, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_integer/ bool is_integer() const noexcept { - return type() == node_t::INTEGER; + return get_node_attrs() & detail::node_attr_bits::int_bit; } /// @brief Tests whether the current basic_node value is of float number type. /// @return true if the type is floating point number, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_float_number/ bool is_float_number() const noexcept { - return type() == node_t::FLOAT_NUMBER; + return get_node_attrs() & detail::node_attr_bits::float_bit; } /// @brief Tests whether the current basic_node value is of string type. /// @return true if the type is string, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_string/ bool is_string() const noexcept { - return type() == node_t::STRING; + return get_node_attrs() & detail::node_attr_bits::string_bit; } /// @brief Tests whether the current basic_node value is of scalar types. /// @return true if the type is scalar, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_scalar/ bool is_scalar() const noexcept { - return !is_sequence() && !is_mapping(); + return get_node_attrs() & detail::node_attr_bits::scalar_bits; } /// @brief Tests whether the current basic_node is an anchor node. /// @return true if the current basic_node is an anchor node, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_anchor/ bool is_anchor() const noexcept { - return m_prop.anchor_status == detail::anchor_status_t::ANCHOR; + return m_attrs & detail::node_attr_bits::anchor_bit; } /// @brief Tests whether the current basic_node is an alias node. /// @return true if the current basic_node is an alias node, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_alias/ bool is_alias() const noexcept { - return m_prop.anchor_status == detail::anchor_status_t::ALIAS; + return m_attrs & detail::node_attr_bits::alias_bit; } /// @brief Tests whether the current basic_node value (sequence, mapping, string) is empty. /// @return true if the node value is empty, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/empty/ bool empty() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->empty(); } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return p_node_value->p_mapping->empty(); } - case node_t::STRING: { + case detail::node_attr_bits::string_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_string != nullptr); return p_node_value->p_string->empty(); } default: - throw fkyaml::type_error("The target node is not of a container type.", type()); + throw fkyaml::type_error("The target node is not of a container type.", get_type()); } } @@ -914,18 +942,18 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/size/ std::size_t size() const { const node_value* p_node_value = get_node_value_ptr(); - switch (type()) { - case node_t::SEQUENCE: + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->size(); - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return p_node_value->p_mapping->size(); - case node_t::STRING: + case detail::node_attr_bits::string_bit: FK_YAML_ASSERT(p_node_value->p_string != nullptr); return p_node_value->p_string->size(); default: - throw fkyaml::type_error("The target node is not of a container type.", type()); + throw fkyaml::type_error("The target node is not of a container type.", get_type()); } } @@ -941,18 +969,16 @@ class basic_node { detail::is_node_compatible_type>>::value, int> = 0> bool contains(KeyType&& key) const { - switch (type()) { - case node_t::MAPPING: { + if FK_YAML_LIKELY (get_node_attrs() & detail::node_attr_bits::map_bit) { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - mapping_type& map = *p_node_value->p_mapping; + const mapping_type& map = *p_node_value->p_mapping; basic_node node_key = std::forward(key); return map.find(std::move(node_key)) != map.end(); } - default: - return false; - } + + return false; } /// @brief Check whether or not this basic_node object has a given key in its inner mapping Node value. @@ -963,17 +989,15 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> bool contains(KeyType&& key) const { - switch (type()) { - case node_t::MAPPING: { + if FK_YAML_LIKELY (get_node_attrs() & detail::node_attr_bits::map_bit) { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - mapping_type& map = *p_node_value->p_mapping; + const mapping_type& map = *p_node_value->p_mapping; return map.find(std::forward(key)) != map.end(); } - default: - return false; - } + + return false; } /// @brief Get a basic_node object with a key of a compatible type. @@ -988,33 +1012,35 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> basic_node& at(KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + sequence_type& seq = *p_node_value->p_sequence; int index = node_key.template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(node_key) != p_node_value->p_mapping->end(); - if (!is_found) { + mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(node_key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(node_key).c_str()); } - return p_node_value->p_mapping->at(node_key); + return map.at(node_key); } /// @brief Get a basic_node object with a key of a compatible type. @@ -1029,33 +1055,35 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> const basic_node& at(KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + const sequence_type& seq = *p_node_value->p_sequence; int index = node_key.template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(node_key) != p_node_value->p_mapping->end(); - if (!is_found) { + const mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(node_key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(node_key).c_str()); } - return p_node_value->p_mapping->at(node_key); + return map.at(node_key); } /// @brief Get a basic_node object with a basic_node key object. @@ -1066,32 +1094,34 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> basic_node& at(KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + sequence_type& seq = *p_node_value->p_sequence; int index = std::forward(key).template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(key) != p_node_value->p_mapping->end(); - if (!is_found) { + mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(key).c_str()); } - return p_node_value->p_mapping->at(key); + return map.at(key); } /// @brief Get a basic_node object with a basic_node key object. @@ -1102,55 +1132,75 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> const basic_node& at(KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + const sequence_type& seq = *p_node_value->p_sequence; int index = std::forward(key).template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(key) != p_node_value->p_mapping->end(); - if (!is_found) { + const mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(key).c_str()); } - return p_node_value->p_mapping->at(key); + return map.at(key); + } + + /// @brief Get the YAML version for this basic_node object. + /// @return The YAML version if already set, `yaml_version_type::VERSION_1_2` otherwise. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version_type/ + yaml_version_type get_yaml_version_type() const noexcept { + return mp_meta->is_version_specified ? mp_meta->version : yaml_version_type::VERSION_1_2; } - /// @brief Get the YAML version specification for this basic_node object. - /// @return The YAML version if any is applied to the basic_node object, `yaml_version_t::VER_1_2` otherwise. + /// @brief Set the YAML version for this basic_node object. + /// @param[in] version The target YAML version. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version_type/ + void set_yaml_version_type(const yaml_version_type version) noexcept { + mp_meta->version = version; + mp_meta->is_version_specified = true; + } + + /// @brief Get the YAML version for this basic_node object. + /// @deprecated Use get_yaml_version_type() function. (since 0.3.12) + /// @return The YAML version if already set, `yaml_version_t::VER_1_2` otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version/ + FK_YAML_DEPRECATED("Since 0.3.12; Use get_yaml_version_type()") yaml_version_t get_yaml_version() const noexcept { - return mp_meta->is_version_specified ? mp_meta->version : yaml_version_t::VER_1_2; + yaml_version_type tmp_type = get_yaml_version_type(); + return detail::convert_from_yaml_version_type(tmp_type); } - /// @brief Set the YAML version specification for this basic_node object. - /// @note If no YAML directive - /// @param[in] A version of the YAML format. + /// @brief Set the YAML version for this basic_node object. + /// @deprecated Use set_yaml_version_type(yaml_version_type) function. (since 0.3.12) + /// @param[in] version The target YAML version. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version/ + FK_YAML_DEPRECATED("Since 0.3.12; Use set_yaml_version_type(const yaml_version_type)") void set_yaml_version(const yaml_version_t version) noexcept { - mp_meta->version = version; - mp_meta->is_version_specified = true; + set_yaml_version_type(detail::convert_to_yaml_version_type(version)); } /// @brief Check whether or not this basic_node object has already had any anchor name. /// @return true if ths basic_node has an anchor name, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/has_anchor_name/ bool has_anchor_name() const noexcept { - return m_prop.anchor_status != detail::anchor_status_t::NONE && !m_prop.anchor.empty(); + return (m_attrs & detail::node_attr_mask::anchoring) && !m_prop.anchor.empty(); } /// @brief Get the anchor name associated with this basic_node object. @@ -1159,7 +1209,7 @@ class basic_node { /// @return The anchor name associated with the node. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_anchor_name/ const std::string& get_anchor_name() const { - if (!has_anchor_name()) { + if FK_YAML_UNLIKELY (!has_anchor_name()) { throw fkyaml::exception("No anchor name has been set."); } return m_prop.anchor; @@ -1171,9 +1221,9 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/ void add_anchor_name(const std::string& anchor_name) { if (is_anchor()) { - m_prop.anchor_status = detail::anchor_status_t::NONE; + m_attrs &= ~detail::node_attr_mask::anchoring; auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); mp_meta.reset(); itr->second.swap(*this); mp_meta->anchor_table.erase(itr); @@ -1185,9 +1235,11 @@ class basic_node { node.swap(*this); p_meta->anchor_table.emplace(anchor_name, std::move(node)); + m_attrs &= ~detail::node_attr_mask::anchoring; + m_attrs |= detail::node_attr_bits::anchor_bit; mp_meta = p_meta; - m_prop.anchor_status = detail::anchor_status_t::ANCHOR; - m_prop.anchor_offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + uint32_t offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + detail::node_attr_bits::set_anchor_offset(offset, m_attrs); m_prop.anchor = anchor_name; } @@ -1197,9 +1249,9 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/ void add_anchor_name(std::string&& anchor_name) { if (is_anchor()) { - m_prop.anchor_status = detail::anchor_status_t::NONE; + m_attrs &= ~detail::node_attr_mask::anchoring; auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); mp_meta.reset(); itr->second.swap(*this); mp_meta->anchor_table.erase(itr); @@ -1211,9 +1263,11 @@ class basic_node { node.swap(*this); p_meta->anchor_table.emplace(anchor_name, std::move(node)); + m_attrs &= ~detail::node_attr_mask::anchoring; + m_attrs |= detail::node_attr_bits::anchor_bit; mp_meta = p_meta; - m_prop.anchor_status = detail::anchor_status_t::ANCHOR; - m_prop.anchor_offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + uint32_t offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + detail::node_attr_bits::set_anchor_offset(offset, m_attrs); m_prop.anchor = std::move(anchor_name); } @@ -1230,7 +1284,7 @@ class basic_node { /// @return The tag name associated with the node. It may be empty. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_tag_name/ const std::string& get_tag_name() const { - if (!has_tag_name()) { + if FK_YAML_UNLIKELY (!has_tag_name()) { throw fkyaml::exception("No tag name has been set."); } return m_prop.tag; @@ -1269,7 +1323,7 @@ class basic_node { auto ret = ValueType(); if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); ConverterType::from_node(itr->second, ret); } else { @@ -1286,7 +1340,7 @@ class basic_node { ReferenceType get_value_ref() { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return itr->second.get_value_ref_impl(static_cast>(nullptr)); } return get_value_ref_impl(static_cast>(nullptr)); @@ -1305,7 +1359,7 @@ class basic_node { ReferenceType get_value_ref() const { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return itr->second.get_value_ref_impl(static_cast>(nullptr)); } return get_value_ref_impl(static_cast>(nullptr)); @@ -1316,7 +1370,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/swap/ void swap(basic_node& rhs) noexcept { using std::swap; - swap(m_node_type, rhs.m_node_type); + swap(m_attrs, rhs.m_attrs); swap(mp_meta, rhs.mp_meta); node_value tmp {}; @@ -1325,9 +1379,7 @@ class basic_node { std::memcpy(&rhs.m_node_value, &tmp, sizeof(node_value)); swap(m_prop.tag, rhs.m_prop.tag); - swap(m_prop.anchor_status, rhs.m_prop.anchor_status); swap(m_prop.anchor, rhs.m_prop.anchor); - swap(m_prop.anchor_offset, rhs.m_prop.anchor_offset); } /// @brief Returns the first iterator of basic_node values of container types (sequence or mapping) from a non-const @@ -1335,19 +1387,19 @@ class basic_node { /// @return An iterator to the first element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/ iterator begin() { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->begin()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->begin()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -1356,19 +1408,19 @@ class basic_node { /// @return A constant iterator to the first element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/ const_iterator begin() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->begin()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->begin()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -1377,19 +1429,19 @@ class basic_node { /// @return An iterator to the past-the end element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/ iterator end() { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->end()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->end()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -1398,19 +1450,19 @@ class basic_node { /// @return A constant iterator to the past-the end element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/ const_iterator end() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->end()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->end()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -1420,134 +1472,143 @@ class basic_node { const node_value* get_node_value_ptr() const { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return &(itr->second.m_node_value); } return &m_node_value; } + detail::node_attr_t get_node_attrs() const { + if (has_anchor_name()) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + return itr->second.m_attrs; + } + return m_attrs; + } + /// @brief Returns reference to the sequence node value. /// @throw fkyaml::exception The node value is not a sequence. /// @return Reference to the sequence node value. sequence_type& get_value_ref_impl(sequence_type* /*unused*/) { - if (!is_sequence()) { - throw fkyaml::type_error("The node value is not a sequence.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::seq_bit) { + return *(m_node_value.p_sequence); } - return *(m_node_value.p_sequence); + throw fkyaml::type_error("The node value is not a sequence.", get_type()); } /// @brief Returns constant reference to the sequence node value. /// @throw fkyaml::exception The node value is not a sequence. /// @return Constant reference to the sequence node value. const sequence_type& get_value_ref_impl(const sequence_type* /*unused*/) const { - if (!is_sequence()) { - throw fkyaml::type_error("The node value is not a sequence.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::seq_bit) { + return *(m_node_value.p_sequence); } - return *(m_node_value.p_sequence); + throw fkyaml::type_error("The node value is not a sequence.", get_type()); } /// @brief Returns reference to the mapping node value. /// @throw fkyaml::exception The node value is not a mapping. /// @return Reference to the mapping node value. mapping_type& get_value_ref_impl(mapping_type* /*unused*/) { - if (!is_mapping()) { - throw fkyaml::type_error("The node value is not a mapping.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::map_bit) { + return *(m_node_value.p_mapping); } - return *(m_node_value.p_mapping); + throw fkyaml::type_error("The node value is not a mapping.", get_type()); } /// @brief Returns constant reference to the mapping node value. /// @throw fkyaml::exception The node value is not a mapping. /// @return Constant reference to the mapping node value. const mapping_type& get_value_ref_impl(const mapping_type* /*unused*/) const { - if (!is_mapping()) { - throw fkyaml::type_error("The node value is not a mapping.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::map_bit) { + return *(m_node_value.p_mapping); } - return *(m_node_value.p_mapping); + throw fkyaml::type_error("The node value is not a mapping.", get_type()); } /// @brief Returns reference to the boolean node value. /// @throw fkyaml::exception The node value is not a boolean. /// @return Reference to the boolean node value. boolean_type& get_value_ref_impl(boolean_type* /*unused*/) { - if (!is_boolean()) { - throw fkyaml::type_error("The node value is not a boolean.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::bool_bit) { + return m_node_value.boolean; } - return m_node_value.boolean; + throw fkyaml::type_error("The node value is not a boolean.", get_type()); } /// @brief Returns reference to the boolean node value. /// @throw fkyaml::exception The node value is not a boolean. /// @return Constant reference to the boolean node value. const boolean_type& get_value_ref_impl(const boolean_type* /*unused*/) const { - if (!is_boolean()) { - throw fkyaml::type_error("The node value is not a boolean.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::bool_bit) { + return m_node_value.boolean; } - return m_node_value.boolean; + throw fkyaml::type_error("The node value is not a boolean.", get_type()); } /// @brief Returns reference to the integer node value. /// @throw fkyaml::exception The node value is not an integer. /// @return Reference to the integer node value. integer_type& get_value_ref_impl(integer_type* /*unused*/) { - if (!is_integer()) { - throw fkyaml::type_error("The node value is not an integer.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::int_bit) { + return m_node_value.integer; } - return m_node_value.integer; + throw fkyaml::type_error("The node value is not an integer.", get_type()); } /// @brief Returns reference to the integer node value. /// @throw fkyaml::exception The node value is not an integer. /// @return Constant reference to the integer node value. const integer_type& get_value_ref_impl(const integer_type* /*unused*/) const { - if (!is_integer()) { - throw fkyaml::type_error("The node value is not an integer.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::int_bit) { + return m_node_value.integer; } - return m_node_value.integer; + throw fkyaml::type_error("The node value is not an integer.", get_type()); } /// @brief Returns reference to the floating point number node value. /// @throw fkyaml::exception The node value is not a floating point number. /// @return Reference to the floating point number node value. float_number_type& get_value_ref_impl(float_number_type* /*unused*/) { - if (!is_float_number()) { - throw fkyaml::type_error("The node value is not a floating point number.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::float_bit) { + return m_node_value.float_val; } - return m_node_value.float_val; + throw fkyaml::type_error("The node value is not a floating point number.", get_type()); } /// @brief Returns reference to the floating point number node value. /// @throw fkyaml::exception The node value is not a floating point number. /// @return Constant reference to the floating point number node value. const float_number_type& get_value_ref_impl(const float_number_type* /*unused*/) const { - if (!is_float_number()) { - throw fkyaml::type_error("The node value is not a floating point number.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::float_bit) { + return m_node_value.float_val; } - return m_node_value.float_val; + throw fkyaml::type_error("The node value is not a floating point number.", get_type()); } /// @brief Returns reference to the string node value. /// @throw fkyaml::exception The node value is not a string. /// @return Reference to the string node value. string_type& get_value_ref_impl(string_type* /*unused*/) { - if (!is_string()) { - throw fkyaml::type_error("The node value is not a string.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::string_bit) { + return *(m_node_value.p_string); } - return *(m_node_value.p_string); + throw fkyaml::type_error("The node value is not a string.", get_type()); } /// @brief Returns reference to the string node value. /// @throw fkyaml::exception The node value is not a string. /// @return Constant reference to the string node value. const string_type& get_value_ref_impl(const string_type* /*unused*/) const { - if (!is_string()) { - throw fkyaml::type_error("The node value is not a string.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::string_bit) { + return *(m_node_value.p_string); } - return *(m_node_value.p_string); + throw fkyaml::type_error("The node value is not a string.", get_type()); } - /// The current node value type. - node_t m_node_type {node_t::NULL_OBJECT}; + /// The current node attributes. + detail::node_attr_t m_attrs {detail::node_attr_bits::default_bits}; /// The shared set of YAML directives applied to this node. mutable std::shared_ptr> mp_meta { std::shared_ptr>(new detail::document_metainfo())}; @@ -1644,7 +1705,7 @@ inline fkyaml::node operator"" _yaml(const char32_t* s, std::size_t n) { return fkyaml::node::deserialize(std::move(s), std::move(s + n)); } -#ifdef FK_YAML_HAS_CHAR8_T +#if FK_YAML_HAS_CHAR8_T /// @brief The user-defined string literal which deserializes a `char8_t` array into a `node` object. /// @param s An input `char8_t` array. /// @param n The size of `s`. diff --git a/include/fkYAML/node_type.hpp b/include/fkYAML/node_type.hpp new file mode 100644 index 00000000..d3e11ab1 --- /dev/null +++ b/include/fkYAML/node_type.hpp @@ -0,0 +1,53 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_NODE_TYPE_HPP_ +#define FK_YAML_NODE_TYPE_HPP_ + +#include + +#include + +FK_YAML_NAMESPACE_BEGIN + +enum class node_type : std::uint32_t { + SEQUENCE, //!< sequence value type + MAPPING, //!< mapping value type + NULL_OBJECT, //!< null value type + BOOLEAN, //!< boolean value type + INTEGER, //!< integer value type + FLOAT, //!< float point value type + STRING, //!< string value type +}; + +inline const char* to_string(node_type t) noexcept { + switch (t) { + case node_type::SEQUENCE: + return "SEQUENCE"; + case node_type::MAPPING: + return "MAPPING"; + case node_type::NULL_OBJECT: + return "NULL_OBJECT"; + case node_type::BOOLEAN: + return "BOOLEAN"; + case node_type::INTEGER: + return "INTEGER"; + case node_type::FLOAT: + return "FLOAT"; + case node_type::STRING: + return "STRING"; + default: // LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +FK_YAML_NAMESPACE_END + +#endif /* FK_YAML_NODE_TYPE_HPP_ */ diff --git a/include/fkYAML/node_value_converter.hpp b/include/fkYAML/node_value_converter.hpp index 5e330c73..8218fe5a 100644 --- a/include/fkYAML/node_value_converter.hpp +++ b/include/fkYAML/node_value_converter.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/ordered_map.hpp b/include/fkYAML/ordered_map.hpp index e7cfb477..0aa94fda 100644 --- a/include/fkYAML/ordered_map.hpp +++ b/include/fkYAML/ordered_map.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/include/fkYAML/yaml_version_type.hpp b/include/fkYAML/yaml_version_type.hpp new file mode 100644 index 00000000..038b030a --- /dev/null +++ b/include/fkYAML/yaml_version_type.hpp @@ -0,0 +1,38 @@ +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_YAML_VERSION_TYPE_HPP_ +#define FK_YAML_YAML_VERSION_TYPE_HPP_ + +#include + +#include + +FK_YAML_NAMESPACE_BEGIN + +enum class yaml_version_type : std::uint32_t { + VERSION_1_1, //!< YAML version 1.1 + VERSION_1_2, //!< YAML version 1.2 +}; + +inline const char* to_string(yaml_version_type t) noexcept { + switch (t) { + case yaml_version_type::VERSION_1_1: + return "VERSION_1_1"; + case yaml_version_type::VERSION_1_2: + return "VERSION_1_2"; + default: // LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +FK_YAML_NAMESPACE_END + +#endif /* FK_YAML_YAML_VERSION_TYPE_HPP_ */ diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index 0322347d..9d8a4446 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -1,6 +1,6 @@ /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -24,7 +24,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -34,7 +34,7 @@ // Check version definitions if already defined. #if defined(FK_YAML_MAJOR_VERSION) && defined(FK_YAML_MINOR_VERSION) && defined(FK_YAML_PATCH_VERSION) -#if FK_YAML_MAJOR_VERSION != 0 || FK_YAML_MINOR_VERSION != 3 || FK_YAML_PATCH_VERSION != 11 +#if FK_YAML_MAJOR_VERSION != 0 || FK_YAML_MINOR_VERSION != 3 || FK_YAML_PATCH_VERSION != 12 #warning Already included a different version of the fkYAML library! #else // define macros to skip defining macros down below. @@ -46,7 +46,7 @@ #define FK_YAML_MAJOR_VERSION 0 #define FK_YAML_MINOR_VERSION 3 -#define FK_YAML_PATCH_VERSION 11 +#define FK_YAML_PATCH_VERSION 12 #define FK_YAML_NAMESPACE_VERSION_CONCAT_IMPL(major, minor, patch) v##major##_##minor##_##patch @@ -74,7 +74,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -88,18 +88,27 @@ // This file is assumed to be included only by version_macros.hpp file. // To avoid redundant inclusion, do not include version_macros.hpp file as the other files do. +// With the MSVC compilers, the value of __cplusplus is by default always "199611L"(C++98). +// To avoid that, the library instead references _MSVC_LANG which is always set a correct value. +// See https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ for more details. +#if defined(_MSVC_LANG) && !defined(__clang__) +#define FK_YAML_CPLUSPLUS _MSVC_LANG +#else +#define FK_YAML_CPLUSPLUS __cplusplus +#endif + // C++ language standard detection (__cplusplus is not yet defined for C++23) // Skip detection if the definitions listed below already exist. #if !defined(FK_YAML_HAS_CXX_20) && !defined(FK_YAML_HAS_CXX_17) && !defined(FK_YAML_HAS_CXX_14) && \ !defined(FK_YAML_CXX_11) -#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && MSVC_LANG >= 202002L) +#if FK_YAML_CPLUSPLUS >= 202002L #define FK_YAML_HAS_CXX_20 #define FK_YAML_HAS_CXX_17 #define FK_YAML_HAS_CXX_14 -#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) +#elif FK_YAML_CPLUSPLUS >= 201703L #define FK_YAML_HAS_CXX_17 #define FK_YAML_HAS_CXX_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) +#elif FK_YAML_CPLUSPLUS >= 201402L #define FK_YAML_HAS_CXX_14 #endif @@ -107,27 +116,90 @@ #define FK_YAML_HAS_CXX_11 #endif -// switch usage of inline variables. Inline variables have been introduced since C++17. +// switch usage of the deprecated attribute. [[deprecated]] is available since C++14. +#if defined(FK_YAML_HAS_CXX_14) +#define FK_YAML_DEPRECATED(msg) [[deprecated(msg)]] +#else +#if defined(_MSC_VER) +#define FK_YAML_DEPRECATED(msg) __declspec(deprecated(msg)) +#elif defined(__GNUC__) || defined(__clang__) +#define FK_YAML_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define FK_YAML_DEPRECATED(msg) +#endif +#endif + +// switch usage of inline variables which have been available since C++17. #if defined(FK_YAML_HAS_CXX_17) #define FK_YAML_INLINE_VAR inline #else #define FK_YAML_INLINE_VAR #endif +// Detect __has_* macros. +// The following macros replace redundant `defined(__has_*) && __has_*(...)`. + #ifdef __has_include -#if __has_include() +#define FK_YAML_HAS_INCLUDE(header) __has_include(header) +#else +#define FK_YAML_HAS_INCLUDE(header) (0) +#endif + +#ifdef __has_builtin +#define FK_YAML_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +#define FK_YAML_HAS_BUILTIN(builtin) (0) +#endif + +#ifdef __has_cpp_attribute +#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) (0) +#endif + +#if FK_YAML_HAS_INCLUDE() // is available since C++20 #include #endif + +// +// C++ feature detections +// + +// switch usages of the std::to_chars()/std::from_chars() functions which have been available since C++17. +#if defined(FK_YAML_HAS_CXX_17) && defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L +#define FK_YAML_HAS_TO_CHARS (1) +#else +#define FK_YAML_HAS_TO_CHARS (0) #endif -// switch usage of char8_t. char8_t has been introduced since C++20 -#if !defined(FK_YAML_HAS_CHAR8_T) -#if defined(FK_YAML_HAS_CXX_20) -#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L -#define FK_YAML_HAS_CHAR8_T +// switch usage of char8_t which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && defined(__cpp_char8_t) && __cpp_char8_t >= 201811L +#define FK_YAML_HAS_CHAR8_T (1) +#else +#define FK_YAML_HAS_CHAR8_T (0) #endif + +// +// C++ attribute detections +// + +// switch usage of [[likely]] C++ attribute which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(likely) >= 201803L +#define FK_YAML_LIKELY(expr) (!!(expr)) [[likely]] +#elif FK_YAML_HAS_BUILTIN(__builtin_expect) +#define FK_YAML_LIKELY(expr) (__builtin_expect(!!(expr), 1)) +#else +#define FK_YAML_LIKELY(expr) (!!(expr)) #endif + +// switch usage of [[unlikely]] C++ attribute which has been available since C++20. +#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(unlikely) >= 201803L +#define FK_YAML_UNLIKELY(expr) (!!(expr)) [[unlikely]] +#elif FK_YAML_HAS_BUILTIN(__builtin_expect) +#define FK_YAML_UNLIKELY(expr) (__builtin_expect(!!(expr), 0)) +#else +#define FK_YAML_UNLIKELY(expr) (!!(expr)) #endif #endif /* FK_YAML_DETAIL_MACROS_CPP_CONFIG_MACROS_HPP_ */ @@ -138,7 +210,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -153,9 +225,9 @@ #ifndef FK_YAML_ASSERT #ifndef NDEBUG #include -#define FK_YAML_ASSERT(x) assert(x) // NOLINT(cppcoreguidelines-macro-usage) +#define FK_YAML_ASSERT(x) assert(x) #else -#define FK_YAML_ASSERT(x) // NOLINT(cppcoreguidelines-macro-usage) +#define FK_YAML_ASSERT(x) #endif #endif @@ -164,7 +236,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -183,7 +255,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -199,7 +271,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -217,7 +289,7 @@ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -465,7 +537,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -476,6 +548,7 @@ FK_YAML_DETAIL_NAMESPACE_END #ifndef FK_YAML_DETAIL_META_TYPE_TRAITS_HPP_ #define FK_YAML_DETAIL_META_TYPE_TRAITS_HPP_ +#include #include #include @@ -573,6 +646,12 @@ struct is_complete_type : std::false_type {}; template struct is_complete_type : std::true_type {}; +/// @brief A utility alias to test if the value type of `ItrType` is `T`. +/// @tparam ItrType An iterator type. +/// @tparam T The target iterator value type. +template +using is_iterator_of = std::is_same::value_type>, T>; + /// @brief A utility struct to generate static constant instance. /// @tparam T A target type for the resulting static constant instance. template @@ -774,10 +853,10 @@ FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_META_NODE_TRAITS_HPP_ */ -// #include +// #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -785,25 +864,35 @@ FK_YAML_DETAIL_NAMESPACE_END /// /// @file -#ifndef FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ -#define FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ +#ifndef FK_YAML_YAML_VERSION_TYPE_HPP_ +#define FK_YAML_YAML_VERSION_TYPE_HPP_ #include // #include -FK_YAML_DETAIL_NAMESPACE_BEGIN +FK_YAML_NAMESPACE_BEGIN -/// @brief Definition of YAML version types. -enum class yaml_version_t : std::uint32_t { - VER_1_1, //!< YAML version 1.1 - VER_1_2, //!< YAML version 1.2 +enum class yaml_version_type : std::uint32_t { + VERSION_1_1, //!< YAML version 1.1 + VERSION_1_2, //!< YAML version 1.2 }; -FK_YAML_DETAIL_NAMESPACE_END +inline const char* to_string(yaml_version_type t) noexcept { + switch (t) { + case yaml_version_type::VERSION_1_1: + return "VERSION_1_1"; + case yaml_version_type::VERSION_1_2: + return "VERSION_1_2"; + default: // LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} -#endif /* FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ */ +FK_YAML_NAMESPACE_END + +#endif /* FK_YAML_YAML_VERSION_TYPE_HPP_ */ FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -812,7 +901,7 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN template ::value>> struct document_metainfo { /// The YAML version used for the YAML document. - yaml_version_t version {yaml_version_t::VER_1_2}; + yaml_version_type version {yaml_version_type::VERSION_1_2}; /// Whether or not the YAML version has been specified. bool is_version_specified {false}; /// The prefix of the primary handle. @@ -832,7 +921,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -844,19 +933,15 @@ FK_YAML_DETAIL_NAMESPACE_END #define FK_YAML_DETAIL_INPUT_DESERIALIZER_HPP_ #include -#include #include -#include #include // #include -// #include - -// #include +// #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -864,559 +949,861 @@ FK_YAML_DETAIL_NAMESPACE_END /// /// @file -#ifndef FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ -#define FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ +// **NOTE FOR LIBARARY DEVELOPERS**: +// Implementations in this header file are intentionally optimized for conversions between YAML scalars and native C++ +// types. So, some implementations don't follow the convensions in the standard C++ functions. For example, octals must +// begin with "0o" (not "0"), which is specified in the YAML spec 1.2. + +#ifndef FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ +#define FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ -#include #include -#include -#include +#include #include -#include -#include // #include -// #include - -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file - -#ifndef FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ -#define FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ +// #include -#include -#include -#include -#include -#include -// #include +#if FK_YAML_HAS_TO_CHARS +// Prefer std::to_chars() and std::from_chars() functions if available. +#include +#else +// Fallback to legacy string conversion functions otherwise. +#include // std::stof(), std::stod(), std::stold() +#endif -// #include +FK_YAML_DETAIL_NAMESPACE_BEGIN -// #include +////////////////////////// +// conv_limits_base // +////////////////////////// -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file +/// @brief A structure which provides limits for conversions between scalars and integers. +/// @note This structure contains common limits in both signed and unsigned integers. +/// @tparam NumBytes The number of bytes for the integer type. +template +struct conv_limits_base {}; -#ifndef FK_YAML_EXCEPTION_HPP_ -#define FK_YAML_EXCEPTION_HPP_ +/// @brief The specialization of conv_limits_base for 1 byte integers, e.g., int8_t, uint8_t. +template <> +struct conv_limits_base<1u> { + /// max characters for octals (0o377) without the prefix part. + static constexpr std::size_t max_chars_oct = 3; + /// max characters for hexadecimals (0xFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 2; + + /// @brief Check if the given octals are safely converted into 1 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3'); + } + + /// @brief Check if the given hexadecimals are safely converted into 1 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; -#include -#include -#include -#include +/// @brief The specialization of conv_limits_base for 2 byte integers, e.g., int16_t, uint16_t. +template <> +struct conv_limits_base<2u> { + /// max characters for octals (0o177777) without the prefix part. + static constexpr std::size_t max_chars_oct = 6; + /// max characters for hexadecimals (0xFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 4; + + /// @brief Check if the given octals are safely converted into 2 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1'); + } + + /// @brief Check if the given hexadecimals are safely converted into 2 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; -// #include +/// @brief The specialization of conv_limits_base for 4 byte integers, e.g., int32_t, uint32_t. +template <> +struct conv_limits_base<4u> { + /// max characters for octals (0o37777777777) without the prefix part. + static constexpr std::size_t max_chars_oct = 11; + /// max characters for hexadecimals (0xFFFFFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 8; + + /// @brief Check if the given octals are safely converted into 4 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3'); + } + + /// @brief Check if the given hexadecimals are safely converted into 4 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file +/// @brief The specialization of conv_limits_base for 8 byte integers, e.g., int64_t, uint64_t. +template <> +struct conv_limits_base<8u> { + /// max characters for octals (0o1777777777777777777777) without the prefix part. + static constexpr std::size_t max_chars_oct = 22; + /// max characters for hexadecimals (0xFFFFFFFFFFFFFFFF) without the prefix part. + static constexpr std::size_t max_chars_hex = 16; + + /// @brief Check if the given octals are safely converted into 8 byte integer. + /// @param octs The pointer to octal characters + /// @param len The length of octal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept { + return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1'); + } + + /// @brief Check if the given hexadecimals are safely converted into 8 byte integer. + /// @param octs The pointer to hexadecimal characters + /// @param len The length of hexadecimal characters + /// @return true is safely convertible, false otherwise. + static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept { + return len <= max_chars_hex; + } +}; -#ifndef FK_YAML_DETAIL_STRING_FORMATTER_HPP_ -#define FK_YAML_DETAIL_STRING_FORMATTER_HPP_ +///////////////////// +// conv_limits // +///////////////////// -#include -#include -#include -#include +/// @brief A structure which provides limits for conversions between scalars and integers. +/// @note This structure contains limits which differs based on signedness. +/// @tparam NumBytes The number of bytes for the integer type. +/// @tparam IsSigned Whether an integer is signed or unsigned +template +struct conv_limits {}; -// #include +/// @brief The specialization of conv_limits for 1 byte signed integers, e.g., int8_t. +template <> +struct conv_limits<1u, true> : conv_limits_base<1u> { + /// with or without sign. + static constexpr bool is_signed = true; + /// max characters for decimals (-128..127) without sign. + static constexpr std::size_t max_chars_dec = 3; -FK_YAML_DETAIL_NAMESPACE_BEGIN + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + // Making this function a static constexpr variable, a link error happens. + // Although the issue has been fixed since C++17, this workaround is necessary to let this functionality work + // with C++11 (the library's default C++ standard version). + // The same thing is applied to similar functions in the other specializations. -inline std::string format(const char* fmt, ...) { - va_list vl; - va_start(vl, fmt); - int size = std::vsnprintf(nullptr, 0, fmt, vl); - va_end(vl); + static constexpr char max_value_chars[] = "127"; + return &max_value_chars[0]; + } - // LCOV_EXCL_START - if (size < 0) { - return ""; + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "128"; + return &min_value_chars[0]; } - // LCOV_EXCL_STOP +}; - std::unique_ptr buffer {new char[size + 1] {}}; +/// @brief The specialization of conv_limits for 1 byte unsigned integers, e.g., uint8_t. +template <> +struct conv_limits<1u, false> : conv_limits_base<1u> { + /// with or without sign. + static constexpr bool is_signed = false; - va_start(vl, fmt); - size = std::vsnprintf(buffer.get(), size + 1, fmt, vl); - va_end(vl); + /// max characters for decimals (0..255) without sign. + static constexpr std::size_t max_chars_dec = 3; - return std::string(buffer.get(), size); -} + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "255"; + return &max_value_chars[0]; + } -FK_YAML_DETAIL_NAMESPACE_END + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } +}; -#endif /* FK_YAML_DETAIL_STRING_FORMATTER_HPP_ */ +/// @brief The specialization of conv_limits for 2 byte signed integers, e.g., int16_t. +template <> +struct conv_limits<2u, true> : conv_limits_base<2u> { + /// with or without sign. + static constexpr bool is_signed = true; -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file + /// max characters for decimals (-32768..32767) without sign. + static constexpr std::size_t max_chars_dec = 5; -#ifndef FK_YAML_DETAIL_TYPES_NODE_T_HPP_ -#define FK_YAML_DETAIL_TYPES_NODE_T_HPP_ + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "32767"; + return &max_value_chars[0]; + } -#include -#include + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "32768"; + return &min_value_chars[0]; + } +}; -// #include +/// @brief The specialization of conv_limits for 2 byte unsigned integers, e.g., uint16_t. +template <> +struct conv_limits<2u, false> : conv_limits_base<2u> { + /// with or without sign. + static constexpr bool is_signed = false; + /// max characters for decimals (0..65535) without sign. + static constexpr std::size_t max_chars_dec = 5; -FK_YAML_DETAIL_NAMESPACE_BEGIN + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "65535"; + return &max_value_chars[0]; + } -/// @brief Definition of node value types. -enum class node_t : std::uint32_t { - SEQUENCE, //!< sequence value type - MAPPING, //!< mapping value type - NULL_OBJECT, //!< null value type - BOOLEAN, //!< boolean value type - INTEGER, //!< integer value type - FLOAT_NUMBER, //!< float number value type - STRING, //!< string value type + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; + } }; -inline const char* to_string(node_t t) noexcept { - switch (t) { - case node_t::SEQUENCE: - return "sequence"; - case node_t::MAPPING: - return "mapping"; - case node_t::NULL_OBJECT: - return "null"; - case node_t::BOOLEAN: - return "boolean"; - case node_t::INTEGER: - return "integer"; - case node_t::FLOAT_NUMBER: - return "float"; - case node_t::STRING: - return "string"; - default: // LCOV_EXCL_LINE - return ""; // LCOV_EXCL_LINE - } -} +/// @brief The specialization of conv_limits for 4 byte signed integers, e.g., int32_t. +template <> +struct conv_limits<4u, true> : conv_limits_base<4u> { + /// with or without sign. + static constexpr bool is_signed = true; -FK_YAML_DETAIL_NAMESPACE_END + /// max characters for decimals (-2147483648..2147483647) without sign. + static constexpr std::size_t max_chars_dec = 10; -#endif /* FK_YAML_DETAIL_TYPES_NODE_T_HPP_ */ + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "2147483647"; + return &max_value_chars[0]; + } + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "2147483648"; + return &min_value_chars[0]; + } +}; -FK_YAML_NAMESPACE_BEGIN +/// @brief The specialization of conv_limits for 4 byte unsigned integers, e.g., uint32_t. +template <> +struct conv_limits<4u, false> : conv_limits_base<4u> { + /// with or without sign. + static constexpr bool is_signed = false; -/// @brief A base exception class used in fkYAML library. -/// @sa https://fktn-k.github.io/fkYAML/api/exception/ -class exception : public std::exception { -public: - /// @brief Construct a new exception object without any error messages. - /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/ - exception() = default; + /// max characters for decimals (0..4294967295) without sign. + static constexpr std::size_t max_chars_dec = 10; - /// @brief Construct a new exception object with an error message. - /// @param[in] msg An error message. - /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/ - explicit exception(const char* msg) noexcept { - if (msg) { - m_error_msg = msg; - } + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "4294967295"; + return &max_value_chars[0]; } -public: - /// @brief Returns an error message internally held. If nothing, a non-null, empty string will be returned. - /// @return An error message internally held. The message might be empty. - /// @sa https://fktn-k.github.io/fkYAML/api/exception/what/ - const char* what() const noexcept override { - return m_error_msg.c_str(); + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; } - -private: - /// An error message holder. - std::string m_error_msg {}; }; -/// @brief An exception class indicating an encoding error. -/// @sa https://fktn-k.github.io/fkYAML/api/exception/invalid_encoding/ -class invalid_encoding : public exception { -public: - /// @brief Construct a new invalid_encoding object for UTF-8 related errors. - /// @param msg An error message. - /// @param u8 The UTF-8 character bytes. - explicit invalid_encoding(const char* msg, const std::initializer_list& u8) noexcept - : exception(generate_error_message(msg, u8).c_str()) { +/// @brief The specialization of conv_limits for 8 byte signed integers, e.g., int64_t. +template <> +struct conv_limits<8u, true> : conv_limits_base<8u> { + /// with or without sign. + static constexpr bool is_signed = true; + + /// max characters for decimals (-9223372036854775808..9223372036854775807) without sign. + static constexpr std::size_t max_chars_dec = 19; + + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "9223372036854775807"; + return &max_value_chars[0]; } - /// @brief Construct a new invalid_encoding object for UTF-16 related errors. - /// @param msg An error message. - /// @param u16_h The first UTF-16 encoded element used for the UTF-8 encoding. - /// @param u16_l The second UTF-16 encoded element used for the UTF-8 encoding. - explicit invalid_encoding(const char* msg, std::array u16) noexcept - : exception(generate_error_message(msg, u16).c_str()) { + /// string representation of min decimal value without sign. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "9223372036854775808"; + return &min_value_chars[0]; } +}; - /// @brief Construct a new invalid_encoding object for UTF-32 related errors. - /// @param msg An error message. - /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding. - explicit invalid_encoding(const char* msg, char32_t u32) noexcept - : exception(generate_error_message(msg, u32).c_str()) { - } +/// @brief The specialization of conv_limits for 8 byte unsigned integers, e.g., uint64_t. +template <> +struct conv_limits<8u, false> : conv_limits_base<8u> { + /// with or without sign. + static constexpr bool is_signed = false; -private: - std::string generate_error_message(const char* msg, const std::initializer_list& u8) const noexcept { - auto itr = u8.begin(); - auto end_itr = u8.end(); - std::string formatted = detail::format("invalid_encoding: %s in=[ 0x%02x", msg, *itr++); - while (itr != end_itr) { - formatted += detail::format(", 0x%02x", *itr++); - } - formatted += " ]"; - return formatted; - } + /// max characters for decimals (0..18446744073709551615) without sign. + static constexpr std::size_t max_chars_dec = 20; - /// @brief Generate an error message from the given parameters for the UTF-16 encoding. - /// @param msg An error message. - /// @param h The first UTF-16 encoded element used for the UTF-8 encoding. - /// @param l The second UTF-16 encoded element used for the UTF-8 encoding. - /// @return A generated error message. - std::string generate_error_message(const char* msg, std::array u16) const noexcept { - // uint16_t is large enough for UTF-16 encoded elements. - return detail::format("invalid_encoding: %s in=[ 0x%04x, 0x%04x ]", msg, uint16_t(u16[0]), uint16_t(u16[1])); + /// string representation of max decimal value. + static const char* max_value_chars_dec() noexcept { + static constexpr char max_value_chars[] = "18446744073709551615"; + return &max_value_chars[0]; } - /// @brief Generate an error message from the given parameters for the UTF-32 encoding. - /// @param msg An error message. - /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding. - /// @return A genereated error message. - std::string generate_error_message(const char* msg, char32_t u32) const noexcept { - // uint32_t is large enough for UTF-32 encoded elements. - return detail::format("invalid_encoding: %s in=0x%08x", msg, uint32_t(u32)); + /// string representation of min decimal value. + static const char* min_value_chars_dec() noexcept { + static constexpr char min_value_chars[] = "0"; + return &min_value_chars[0]; } }; -/// @brief An exception class indicating an error in parsing. -class parse_error : public exception { -public: - explicit parse_error(const char* msg, uint32_t lines, uint32_t cols_in_line) noexcept - : exception(generate_error_message(msg, lines, cols_in_line).c_str()) { +////////////////////////// +// scalar <--> null // +////////////////////////// + +/// @brief Converts a scalar into a null value +/// @tparam CharItr Type of char iterators. Its value type must be `char` (maybe cv-qualified). +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param /*unused*/ The null value holder (unused since it can only have `nullptr`) +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool aton(CharItr begin, CharItr end, std::nullptr_t& /*unused*/) noexcept { + static_assert(is_iterator_of::value, "aton() accepts iterators for char type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; } -private: - std::string generate_error_message(const char* msg, uint32_t lines, uint32_t cols_in_line) const noexcept { - return detail::format("parse_error: %s (at line %u, column %u)", msg, lines, cols_in_line); - } -}; + uint32_t len = static_cast(std::distance(begin, end)); -/// @brief An exception class indicating an invalid type conversion. -/// @sa https://fktn-k.github.io/fkYAML/api/exception/type_error/ -class type_error : public exception { -public: - /// @brief Construct a new type_error object with an error message and a node type. - /// @param[in] msg An error message. - /// @param[in] type The type of a source node value. - explicit type_error(const char* msg, detail::node_t type) noexcept - : exception(generate_error_message(msg, type).c_str()) { + // This path is the most probable case, so check it first. + if FK_YAML_LIKELY (len == 4) { + const char* p_begin = &*begin; + return (std::strncmp(p_begin, "null", 4) == 0) || (std::strncmp(p_begin, "Null", 4) == 0) || + (std::strncmp(p_begin, "NULL", 4) == 0); } -private: - /// @brief Generate an error message from given parameters. - /// @param msg An error message. - /// @param type The type of a source node value. - /// @return A generated error message. - std::string generate_error_message(const char* msg, detail::node_t type) const noexcept { - return detail::format("type_error: %s type=%s", msg, detail::to_string(type)); + if (len == 1) { + return *begin == '~'; } -}; -class out_of_range : public exception { -public: - explicit out_of_range(int index) noexcept - : exception(generate_error_message(index).c_str()) { + return false; +} + +///////////////////////////// +// scalar <--> boolean // +///////////////////////////// + +/// @brief Converts a scalar into a boolean value +/// @tparam CharItr The type of char iterators. Its value type must be `char` (maybe cv-qualified). +/// @tparam BoolType The output boolean type. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param boolean The boolean value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atob(CharItr begin, CharItr end, BoolType& boolean) noexcept { + static_assert(is_iterator_of::value, "atob() accepts iterators for char type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; } - explicit out_of_range(const char* key) noexcept - : exception(generate_error_message(key).c_str()) { + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + + if (len == 4) { + bool is_true_scalar = (std::strncmp(p_begin, "true", 4) == 0) || (std::strncmp(p_begin, "True", 4) == 0) || + (std::strncmp(p_begin, "TRUE", 4) == 0); + + if FK_YAML_LIKELY (is_true_scalar) { + boolean = static_cast(true); + } + return is_true_scalar; } -private: - std::string generate_error_message(int index) { - return detail::format("out_of_range: index %d is out of range", index); + if (len == 5) { + bool is_false_scalar = (std::strncmp(p_begin, "false", 5) == 0) || (std::strncmp(p_begin, "False", 5) == 0) || + (std::strncmp(p_begin, "FALSE", 5) == 0); + + if FK_YAML_LIKELY (is_false_scalar) { + boolean = static_cast(false); + } + return is_false_scalar; } - std::string generate_error_message(const char* key) { - return detail::format("out_of_range: key \'%s\' is not found.", key); + return false; +} + +///////////////////////////// +// scalar <--> integer // +///////////////////////////// + +// +// scalar --> decimals +// + +/// @brief Converts a scalar into decimals. This is common implementation for both signed/unsigned integer types. +/// @warning +/// This function does NOT care about overflows if IntType is unsigned. The source string value must be validated +/// beforehand by calling either atoi_dec_pos() or atoi_dec_neg() functions. +/// Furthermore, `p_begin` and `p_end` must NOT be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_unchecked(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, + "atoi_dec_unchecked() accepts non-boolean integral types as an output type"); + + i = 0; + do { + char c = *p_begin; + if FK_YAML_UNLIKELY (c < '0' || '9' < c) { + return false; + } + // Overflow is intentional when the IntType is signed. + i = i * IntType(10) + IntType(c - '0'); + } while (++p_begin != p_end); + + return true; +} + +/// @brief Converts a scalar into positive decimals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_pos(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_dec_pos() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; } -}; -class invalid_tag : public exception { -public: - explicit invalid_tag(const char* msg, const char* tag) - : exception(generate_error_message(msg, tag).c_str()) { + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) { + // Overflow will happen. + return false; } -private: - std::string generate_error_message(const char* msg, const char* tag) { - return detail::format("invalid_tag: %s tag=%s", msg, tag); + if (len == conv_limits_type::max_chars_dec) { + const char* p_max_value_chars_dec = conv_limits_type::max_value_chars_dec(); + + for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) { + if (p_begin[idx] < p_max_value_chars_dec[idx]) { + // No need to check the lower digits. Overflow will no longer happen. + break; + } + + if FK_YAML_UNLIKELY (p_begin[idx] > p_max_value_chars_dec[idx]) { + // Overflow will happen. + return false; + } + } } -}; -FK_YAML_NAMESPACE_END + return atoi_dec_unchecked(p_begin, p_end, i); +} -#endif /* FK_YAML_EXCEPTION_HPP_ */ +/// @brief Converts a scalar into negative decimals. This function executes bounds check to avoid underflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It must be signed. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_dec_neg(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_dec_neg() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; + } + using conv_limits_type = conv_limits::value>; -FK_YAML_DETAIL_NAMESPACE_BEGIN + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) { + // Underflow will happen. + return false; + } -/// @brief Convert a string YAML token to a ValueType object. -/// @tparam ValueType A target value type. -/// @tparam CharType The type of characters in a source string. -template -inline ValueType from_string(const std::basic_string& s, type_tag /*unused*/); + if (len == conv_limits_type::max_chars_dec) { + const char* p_min_value_chars_dec = conv_limits_type::min_value_chars_dec(); -/// @brief Specialization of from_string() for null values with std::string -/// @tparam N/A -template <> -inline std::nullptr_t from_string(const std::string& s, type_tag /*unused*/) { - if (s == "null" || s == "Null" || s == "NULL" || s == "~") { - return nullptr; + for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) { + if (p_begin[idx] < p_min_value_chars_dec[idx]) { + // No need to check the lower digits. Underflow will no longer happen. + break; + } + + if FK_YAML_UNLIKELY (p_begin[idx] > p_min_value_chars_dec[idx]) { + // Underflow will happen. + return false; + } + } } - throw exception("Cannot convert a string into a null value."); + return atoi_dec_unchecked(p_begin, p_end, i); } -/// @brief Specialization of from_string() for boolean values with std::string. -/// @tparam N/A -template <> -inline bool from_string(const std::string& s, type_tag /*unused*/) { - if (s == "true" || s == "True" || s == "TRUE") { - return true; +// +// scalar --> octals +// + +/// @brief Converts a scalar into octals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_oct(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_oct() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; } - if (s == "false" || s == "False" || s == "FALSE") { + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (!conv_limits_type::check_if_octs_safe(p_begin, len)) { return false; } - throw exception("Cannot convert a string into a boolean value."); -} + i = 0; + do { + char c = *p_begin; + if FK_YAML_UNLIKELY (c < '0' || '7' < c) { + return false; + } + i = i * IntType(8) + IntType(c - '0'); + } while (++p_begin != p_end); -/// @brief Specialization of from_string() for int values with std::string. -/// @tparam N/A -template <> -inline int from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long ret = 0; + return true; +} - try { - ret = std::stoi(s, &idx, 0); +// +// scalar --> hexadecimals +// + +/// @brief Converts a scalar into hexadecimals. This function executes bounds check to avoid overflow. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi_hex(const char* p_begin, const char* p_end, IntType& i) noexcept { + static_assert( + is_non_bool_integral::value, "atoi_hex() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (p_begin == p_end) { + return false; } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an int value."); + + using conv_limits_type = conv_limits::value>; + + std::size_t len = static_cast(p_end - p_begin); + if FK_YAML_UNLIKELY (!conv_limits_type::check_if_hexs_safe(p_begin, len)) { + return false; } - return ret; -} + i = 0; + do { + char c = *p_begin; + IntType ci = 0; + if ('0' <= c && c <= '9') { + ci = IntType(c - '0'); + } + else if ('A' <= c && c <= 'F') { + ci = IntType(c - 'A' + 10); + } + else if ('a' <= c && c <= 'f') { + ci = IntType(c - 'a' + 10); + } + else { + return false; + } + i = i * IntType(16) + ci; + } while (++p_begin != p_end); -/// @brief Specialization of from_string() for long values with std::string. -/// @tparam N/A -template <> -inline long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long ret = 0; + return true; +} - try { - ret = std::stol(s, &idx, 0); +// +// atoi() & itoa() +// + +/// @brief Converts a scalar into integers. This function executes bounds check to avoid overflow/underflow. +/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified). +/// @tparam IntType The output integer type. It can be either signed or unsigned. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param i The output integer value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atoi(CharItr begin, CharItr end, IntType& i) noexcept { + static_assert(is_iterator_of::value, "atoi() accepts iterators for char type"); + static_assert(is_non_bool_integral::value, "atoi() accepts non-boolean integral types as an output type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a long value."); + + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + const char* p_end = p_begin + len; + + char first = *begin; + if (first == '+') { + return atoi_dec_pos(p_begin + 1, p_end, i); } - return ret; -} + if (first == '-') { + if (!std::numeric_limits::is_signed) { + return false; + } -/// @brief Specialization of from_string() for long long values with std::string. -/// @tparam N/A -template <> -inline long long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - long long ret = 0; + bool success = atoi_dec_neg(p_begin + 1, p_end, i); + if (success) { + i *= IntType(-1); + } - try { - ret = std::stoll(s, &idx, 0); + return success; } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a long long value."); + + if (first != '0') { + return atoi_dec_pos(p_begin, p_end, i); + } + else if (p_begin + 1 != p_end) { + switch (*(p_begin + 1)) { + case 'o': + return atoi_oct(p_begin + 2, p_end, i); + case 'x': + return atoi_hex(p_begin + 2, p_end, i); + default: + // The YAML spec doesn't allow decimals starting with 0. + return false; + } } - return ret; + i = 0; + return true; } -/// @brief Partial specialization of from_string() for other signed integer types with std::string. -/// @tparam SignedIntType A signed integer type other than long long. -template -inline enable_if_t< - conjunction< - is_non_bool_integral, std::is_signed, negation>, - negation>, negation>>::value, - SignedIntType> -from_string(const std::string& s, type_tag /*unused*/) { - const auto tmp_ret = from_string(s, type_tag {}); - if (static_cast(std::numeric_limits::max()) < tmp_ret) { - throw exception("Failed to convert a long long value into a SignedIntegerType value."); - } - - return static_cast(tmp_ret); +/////////////////////////// +// scalar <--> float // +/////////////////////////// + +/// @brief Set an infinite `float` value based on the given signedness. +/// @param f The output `float` value holder. +/// @param sign Whether the infinite value should be positive or negative. +inline void set_infinity(float& f, const float sign) noexcept { + f = std::numeric_limits::infinity() * sign; } -/// @brief Specialization of from_string() for unsigned long values with std::string. -/// @tparam N/A -template <> -inline unsigned long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - unsigned long ret = 0; +/// @brief Set an infinite `double` value based on the given signedness. +/// @param f The output `double` value holder. +/// @param sign Whether the infinite value should be positive or negative. +inline void set_infinity(double& f, const double sign) noexcept { + f = std::numeric_limits::infinity() * sign; +} - try { - ret = std::stoul(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an unsigned long value."); - } +/// @brief Set a NaN `float` value. +/// @param f The output `float` value holder. +inline void set_nan(float& f) noexcept { + f = std::nanf(""); +} - return ret; +/// @brief Set a NaN `double` value. +/// @param f The output `double` value holder. +inline void set_nan(double& f) noexcept { + f = std::nan(""); } -/// @brief Specialization of from_string() for unsigned long long values with std::string. -/// @tparam N/A -template <> -inline unsigned long long from_string(const std::string& s, type_tag /*unused*/) { - std::size_t idx = 0; - unsigned long long ret = 0; +#if FK_YAML_HAS_TO_CHARS - try { - ret = std::stoull(s, &idx, 0); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into an unsigned long long value."); +/// @brief Converts a scalar into a floating point value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output floating point value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atof_impl(const char* p_begin, const char* p_end, FloatType& f) noexcept { + static_assert(std::is_floating_point_v, "atof_impl() accepts floating point types as an output type"); + if (auto [ptr, ec] = std::from_chars(p_begin, p_end, f); ec == std::errc {}) { + return ptr == p_end; } + return false; +} + +#else - return ret; +/// @brief Converts a scalar into a `float` value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output `float` value holder. +/// @return true if the conversion completes successfully, false otherwise. +inline bool atof_impl(const char* p_begin, const char* p_end, float& f) { + std::size_t idx = 0; + f = std::stof(std::string(p_begin, p_end), &idx); + return idx == static_cast(p_end - p_begin); } -/// @brief Partial specialization of from_string() for other unsigned integer types with std::string. -/// @tparam UnsignedIntType An unsigned integer type other than unsigned long long. -template -inline enable_if_t< - conjunction< - is_non_bool_integral, std::is_unsigned, - negation>, - negation>>::value, - UnsignedIntType> -from_string(const std::string& s, type_tag /*unused*/) { - const auto tmp_ret = from_string(s, type_tag {}); - if (static_cast(std::numeric_limits::max()) < tmp_ret) { - throw exception("Failed to convert an unsigned long long into an unsigned integer value."); - } - - return static_cast(tmp_ret); +/// @brief Converts a scalar into a `double` value. +/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function. +/// @param p_begin The pointer to the first element of the scalar. +/// @param p_end The pointer to the past-the-end element of the scalar. +/// @param f The output `double` value holder. +/// @return true if the conversion completes successfully, false otherwise. +inline bool atof_impl(const char* p_begin, const char* p_end, double& f) { + std::size_t idx = 0; + f = std::stod(std::string(p_begin, p_end), &idx); + return idx == static_cast(p_end - p_begin); } -/// @brief Specialization of from_string() for float values with std::string. -/// @tparam N/A -template <> -inline float from_string(const std::string& s, type_tag /*unused*/) { - if (s == ".inf" || s == ".Inf" || s == ".INF") { - return std::numeric_limits::infinity(); +#endif // FK_YAML_HAS_TO_CHARS + +/// @brief Converts a scalar into a floating point value. +/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified). +/// @tparam FloatType The output floatint point value type. +/// @param begin The iterator to the first element of the scalar. +/// @param end The iterator to the past-the-end element of the scalar. +/// @param f The output floating point value holder. +/// @return true if the conversion completes successfully, false otherwise. +template +inline bool atof(CharItr begin, CharItr end, FloatType& f) noexcept(noexcept(atof_impl(&*begin, &*begin, f))) { + static_assert(is_iterator_of::value, "atof() accepts iterators for char type"); + static_assert(std::is_floating_point::value, "atof() accepts floating point types as an output type"); + + if FK_YAML_UNLIKELY (begin == end) { + return false; } - if (s == "-.inf" || s == "-.Inf" || s == "-.INF") { - static_assert(std::numeric_limits::is_iec559, "IEEE 754 required."); - return -1 * std::numeric_limits::infinity(); - } + uint32_t len = static_cast(std::distance(begin, end)); + const char* p_begin = &*begin; + const char* p_end = p_begin + len; + + if (*p_begin == '-' || *p_begin == '+') { + if (len == 5) { + const char* p_from_second = p_begin + 1; + bool is_inf_scalar = (std::strncmp(p_from_second, ".inf", 4) == 0) || + (std::strncmp(p_from_second, ".Inf", 4) == 0) || + (std::strncmp(p_from_second, ".INF", 4) == 0); - if (s == ".nan" || s == ".NaN" || s == ".NAN") { - return std::nanf(""); + if (is_inf_scalar) { + set_infinity(f, *p_begin == '-' ? FloatType(-1.) : FloatType(1.)); + return true; + } + } } + else if (len == 4) { + bool is_inf_scalar = (std::strncmp(p_begin, ".inf", 4) == 0) || (std::strncmp(p_begin, ".Inf", 4) == 0) || + (std::strncmp(p_begin, ".INF", 4) == 0); + bool is_nan_scalar = false; + if (!is_inf_scalar) { + is_nan_scalar = (std::strncmp(p_begin, ".nan", 4) == 0) || (std::strncmp(p_begin, ".NaN", 4) == 0) || + (std::strncmp(p_begin, ".NAN", 4) == 0); + } - std::size_t idx = 0; - float ret = 0.0f; + if (is_inf_scalar) { + set_infinity(f, FloatType(1.)); + return true; + } + + if (is_nan_scalar) { + set_nan(f); + return true; + } + } +#if FK_YAML_HAS_TO_CHARS + return atof_impl(p_begin, p_end, f); +#else + bool success = false; try { - ret = std::stof(s, &idx); + success = atof_impl(p_begin, p_end, f); } catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a float value."); + success = false; } - return ret; + return success; +#endif } -/// @brief Specialization of from_string() for double values with std::string. -/// @tparam N/A -template <> -inline double from_string(const std::string& s, type_tag /*unused*/) { - if (s == ".inf" || s == ".Inf" || s == ".INF") { - return std::numeric_limits::infinity(); - } +FK_YAML_DETAIL_NAMESPACE_END - if (s == "-.inf" || s == "-.Inf" || s == "-.INF") { - static_assert(std::numeric_limits::is_iec559, "IEEE 754 required."); - return -1 * std::numeric_limits::infinity(); - } +#endif /* FK_YAML_CONVERSIONS_SCALAR_CONV_HPP_ */ - if (s == ".nan" || s == ".NaN" || s == ".NAN") { - return std::nan(""); - } +// #include - std::size_t idx = 0; - double ret = 0.0; +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file - try { - ret = std::stod(s, &idx); - } - catch (const std::exception& /*unused*/) { - throw exception("Failed to convert a string into a double value."); - } +#ifndef FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ +#define FK_YAML_DETAIL_INPUT_LEXICAL_ANALIZER_HPP_ - return ret; -} +#include +#include +#include -FK_YAML_DETAIL_NAMESPACE_END +// #include -#endif /* FK_YAML_DETAIL_CONVERSIONS_FROM_STRING_HPP_ */ +// #include // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -1442,12 +1829,12 @@ class uri_encoding { /// @param begin An iterator to the first element of the character sequence. /// @param end An iterator to the past-the-end element of the character sequence. /// @return true if all the characters are valid, false otherwise. - static bool validate(std::string::const_iterator begin, std::string::const_iterator end) noexcept { + static bool validate(const char* begin, const char* end) noexcept { if (begin == end) { return true; } - std::string::const_iterator current = begin; + const char* current = begin; for (; current != end; ++current) { if (*current == '%') { @@ -1473,7 +1860,7 @@ class uri_encoding { /// @param begin An iterator to the first octet. /// @param end An iterator to the past-the-end element of the whole character sequence. /// @return true if the octets are valid, false otherwise. - static bool validate_octets(std::string::const_iterator& begin, std::string::const_iterator& end) { + static bool validate_octets(const char*& begin, const char*& end) { for (int i = 0; i < 2; i++, ++begin) { if (begin == end) { return false; @@ -1546,7 +1933,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -1563,8 +1950,402 @@ FK_YAML_DETAIL_NAMESPACE_END // #include // #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_EXCEPTION_HPP_ +#define FK_YAML_EXCEPTION_HPP_ + +#include +#include +#include +#include + +// #include + +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_STRING_FORMATTER_HPP_ +#define FK_YAML_DETAIL_STRING_FORMATTER_HPP_ + +#include +#include +#include +#include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +inline std::string format(const char* fmt, ...) { + va_list vl; + va_start(vl, fmt); + int size = std::vsnprintf(nullptr, 0, fmt, vl); + va_end(vl); + + // LCOV_EXCL_START + if (size < 0) { + return ""; + } + // LCOV_EXCL_STOP + + std::unique_ptr buffer {new char[size + 1] {}}; + + va_start(vl, fmt); + size = std::vsnprintf(buffer.get(), size + 1, fmt, vl); + va_end(vl); + + return std::string(buffer.get(), size); +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_STRING_FORMATTER_HPP_ */ + +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_TYPES_NODE_T_HPP_ +#define FK_YAML_DETAIL_TYPES_NODE_T_HPP_ + +#include +#include + +// #include + +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_NODE_TYPE_HPP_ +#define FK_YAML_NODE_TYPE_HPP_ + +#include + +// #include + + +FK_YAML_NAMESPACE_BEGIN + +enum class node_type : std::uint32_t { + SEQUENCE, //!< sequence value type + MAPPING, //!< mapping value type + NULL_OBJECT, //!< null value type + BOOLEAN, //!< boolean value type + INTEGER, //!< integer value type + FLOAT, //!< float point value type + STRING, //!< string value type +}; + +inline const char* to_string(node_type t) noexcept { + switch (t) { + case node_type::SEQUENCE: + return "SEQUENCE"; + case node_type::MAPPING: + return "MAPPING"; + case node_type::NULL_OBJECT: + return "NULL_OBJECT"; + case node_type::BOOLEAN: + return "BOOLEAN"; + case node_type::INTEGER: + return "INTEGER"; + case node_type::FLOAT: + return "FLOAT"; + case node_type::STRING: + return "STRING"; + default: // LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +FK_YAML_NAMESPACE_END + +#endif /* FK_YAML_NODE_TYPE_HPP_ */ + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Definition of node value types. +enum class node_t : std::uint32_t { + SEQUENCE, //!< sequence value type + MAPPING, //!< mapping value type + NULL_OBJECT, //!< null value type + BOOLEAN, //!< boolean value type + INTEGER, //!< integer value type + FLOAT_NUMBER, //!< float number value type + STRING, //!< string value type +}; + +inline const char* to_string(node_t t) noexcept { + switch (t) { + case node_t::SEQUENCE: + return "sequence"; + case node_t::MAPPING: + return "mapping"; + case node_t::NULL_OBJECT: + return "null"; + case node_t::BOOLEAN: + return "boolean"; + case node_t::INTEGER: + return "integer"; + case node_t::FLOAT_NUMBER: + return "float"; + case node_t::STRING: + return "string"; + default: // LCOV_EXCL_LINE + return ""; // LCOV_EXCL_LINE + } +} + +inline node_t convert_from_node_type(node_type t) { + switch (t) { + case node_type::SEQUENCE: + return node_t::SEQUENCE; + case node_type::MAPPING: + return node_t::MAPPING; + case node_type::NULL_OBJECT: + return node_t::NULL_OBJECT; + case node_type::BOOLEAN: + return node_t::BOOLEAN; + case node_type::INTEGER: + return node_t::INTEGER; + case node_type::FLOAT: + return node_t::FLOAT_NUMBER; + case node_type::STRING: + return node_t::STRING; + default: // LCOV_EXCL_LINE + return node_t::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + +inline node_type convert_to_node_type(node_t t) { + switch (t) { + case node_t::SEQUENCE: + return node_type::SEQUENCE; + case node_t::MAPPING: + return node_type::MAPPING; + case node_t::NULL_OBJECT: + return node_type::NULL_OBJECT; + case node_t::BOOLEAN: + return node_type::BOOLEAN; + case node_t::INTEGER: + return node_type::INTEGER; + case node_t::FLOAT_NUMBER: + return node_type::FLOAT; + case node_t::STRING: + return node_type::STRING; + default: // LCOV_EXCL_LINE + return node_type::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_TYPES_NODE_T_HPP_ */ + + +FK_YAML_NAMESPACE_BEGIN + +/// @brief A base exception class used in fkYAML library. +/// @sa https://fktn-k.github.io/fkYAML/api/exception/ +class exception : public std::exception { +public: + /// @brief Construct a new exception object without any error messages. + /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/ + exception() = default; + + /// @brief Construct a new exception object with an error message. + /// @param[in] msg An error message. + /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/ + explicit exception(const char* msg) noexcept { + if (msg) { + m_error_msg = msg; + } + } + +public: + /// @brief Returns an error message internally held. If nothing, a non-null, empty string will be returned. + /// @return An error message internally held. The message might be empty. + /// @sa https://fktn-k.github.io/fkYAML/api/exception/what/ + const char* what() const noexcept override { + return m_error_msg.c_str(); + } + +private: + /// An error message holder. + std::string m_error_msg {}; +}; + +/// @brief An exception class indicating an encoding error. +/// @sa https://fktn-k.github.io/fkYAML/api/exception/invalid_encoding/ +class invalid_encoding : public exception { +public: + /// @brief Construct a new invalid_encoding object for UTF-8 related errors. + /// @param msg An error message. + /// @param u8 The UTF-8 character bytes. + explicit invalid_encoding(const char* msg, const std::initializer_list& u8) noexcept + : exception(generate_error_message(msg, u8).c_str()) { + } + + /// @brief Construct a new invalid_encoding object for UTF-16 related errors. + /// @param msg An error message. + /// @param u16_h The first UTF-16 encoded element used for the UTF-8 encoding. + /// @param u16_l The second UTF-16 encoded element used for the UTF-8 encoding. + explicit invalid_encoding(const char* msg, std::array u16) noexcept + : exception(generate_error_message(msg, u16).c_str()) { + } + + /// @brief Construct a new invalid_encoding object for UTF-32 related errors. + /// @param msg An error message. + /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding. + explicit invalid_encoding(const char* msg, char32_t u32) noexcept + : exception(generate_error_message(msg, u32).c_str()) { + } + +private: + std::string generate_error_message(const char* msg, const std::initializer_list& u8) const noexcept { + auto itr = u8.begin(); + auto end_itr = u8.end(); + std::string formatted = detail::format("invalid_encoding: %s in=[ 0x%02x", msg, *itr++); + while (itr != end_itr) { + formatted += detail::format(", 0x%02x", *itr++); + } + formatted += " ]"; + return formatted; + } + + /// @brief Generate an error message from the given parameters for the UTF-16 encoding. + /// @param msg An error message. + /// @param h The first UTF-16 encoded element used for the UTF-8 encoding. + /// @param l The second UTF-16 encoded element used for the UTF-8 encoding. + /// @return A generated error message. + std::string generate_error_message(const char* msg, std::array u16) const noexcept { + // uint16_t is large enough for UTF-16 encoded elements. + return detail::format("invalid_encoding: %s in=[ 0x%04x, 0x%04x ]", msg, uint16_t(u16[0]), uint16_t(u16[1])); + } + + /// @brief Generate an error message from the given parameters for the UTF-32 encoding. + /// @param msg An error message. + /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding. + /// @return A genereated error message. + std::string generate_error_message(const char* msg, char32_t u32) const noexcept { + // uint32_t is large enough for UTF-32 encoded elements. + return detail::format("invalid_encoding: %s in=0x%08x", msg, uint32_t(u32)); + } +}; + +/// @brief An exception class indicating an error in parsing. +class parse_error : public exception { +public: + explicit parse_error(const char* msg, uint32_t lines, uint32_t cols_in_line) noexcept + : exception(generate_error_message(msg, lines, cols_in_line).c_str()) { + } + +private: + std::string generate_error_message(const char* msg, uint32_t lines, uint32_t cols_in_line) const noexcept { + return detail::format("parse_error: %s (at line %u, column %u)", msg, lines, cols_in_line); + } +}; + +/// @brief An exception class indicating an invalid type conversion. +/// @sa https://fktn-k.github.io/fkYAML/api/exception/type_error/ +class type_error : public exception { +public: + /// @brief Construct a new type_error object with an error message and a node type. + /// @param[in] msg An error message. + /// @param[in] type The type of a source node value. + explicit type_error(const char* msg, node_type type) noexcept + : exception(generate_error_message(msg, type).c_str()) { + } + + /// @brief Construct a new type_error object with an error message and a node type. + /// @deprecated Use type_error(const char*, node_type) constructor. (since 0.3.12). + /// @param[in] msg An error message. + /// @param[in] type The type of a source node value. + FK_YAML_DEPRECATED("Since 0.3.12; Use explicit type_error(const char*, node_type)") + explicit type_error(const char* msg, detail::node_t type) noexcept + : type_error(msg, detail::convert_to_node_type(type)) { + } + +private: + /// @brief Generate an error message from given parameters. + /// @param msg An error message. + /// @param type The type of a source node value. + /// @return A generated error message. + std::string generate_error_message(const char* msg, node_type type) const noexcept { + return detail::format("type_error: %s type=%s", msg, to_string(type)); + } +}; + +class out_of_range : public exception { +public: + explicit out_of_range(int index) noexcept + : exception(generate_error_message(index).c_str()) { + } + + explicit out_of_range(const char* key) noexcept + : exception(generate_error_message(key).c_str()) { + } + +private: + std::string generate_error_message(int index) { + return detail::format("out_of_range: index %d is out of range", index); + } + + std::string generate_error_message(const char* key) { + return detail::format("out_of_range: key \'%s\' is not found.", key); + } +}; + +class invalid_tag : public exception { +public: + explicit invalid_tag(const char* msg, const char* tag) + : exception(generate_error_message(msg, tag).c_str()) { + } + +private: + std::string generate_error_message(const char* msg, const char* tag) { + return detail::format("invalid_tag: %s tag=%s", msg, tag); + } +}; + +FK_YAML_NAMESPACE_END + +#endif /* FK_YAML_EXCEPTION_HPP_ */ + - FK_YAML_DETAIL_NAMESPACE_BEGIN ///////////////////////// @@ -1839,7 +2620,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -1867,7 +2648,7 @@ class yaml_escaper { using iterator = ::std::string::const_iterator; public: - static bool unescape(iterator& begin, iterator end, std::string& buff) { + static bool unescape(const char*& begin, const char* end, std::string& buff) { FK_YAML_ASSERT(*begin == '\\' && std::distance(begin, end) > 0); bool ret = true; @@ -1924,7 +2705,7 @@ class yaml_escaper { case 'x': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 1, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -1932,7 +2713,7 @@ class yaml_escaper { case 'u': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 2, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -1940,7 +2721,7 @@ class yaml_escaper { case 'U': { char32_t codepoint {0}; ret = extract_codepoint(begin, end, 4, codepoint); - if (ret) { + if FK_YAML_LIKELY (ret) { unescape_escaped_unicode(codepoint, buff); } break; @@ -1954,7 +2735,7 @@ class yaml_escaper { return ret; } - static ::std::string escape(iterator begin, iterator end, bool& is_escaped) { + static ::std::string escape(const char* begin, const char* end, bool& is_escaped) { ::std::string escaped {}; escaped.reserve(std::distance(begin, end)); for (; begin != end; ++begin) { @@ -2153,7 +2934,7 @@ class yaml_escaper { return false; } - static bool extract_codepoint(iterator& begin, iterator end, int bytes_to_read, char32_t& codepoint) { + static bool extract_codepoint(const char*& begin, const char* end, int bytes_to_read, char32_t& codepoint) { bool has_enough_room = static_cast(std::distance(begin, end)) >= (bytes_to_read - 1); if (!has_enough_room) { return false; @@ -2187,10 +2968,10 @@ FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_ENCODINGS_YAML_ESCAPER_HPP_ */ -// #include +// #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -2198,20 +2979,17 @@ FK_YAML_DETAIL_NAMESPACE_END /// /// @file -#ifndef FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ -#define FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ +#ifndef FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ +#define FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ #include -#include // #include -// #include - -// #include +// #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -2219,365 +2997,963 @@ FK_YAML_DETAIL_NAMESPACE_END /// /// @file -#ifndef FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ -#define FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ +#ifndef FK_YAML_DETAIL_STR_VIEW_HPP_ +#define FK_YAML_DETAIL_STR_VIEW_HPP_ + +#include +#include + +// #include + +// #include + +// #include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Non owning view into constant character sequence. +/// @note +/// This class is a minimal implementation of std::basic_string_view which has been available since C++17 +/// but pretty useful and efficient for referencing/investigating character sequences. +/// @warning +/// This class intentionally omits a lot of value checks to improve efficiency. Necessary checks should be +/// made before calling this class' APIs for safety. +/// @tparam CharT Character type +/// @tparam Traits Character traits type which defaults to std::char_traits. +template > +class basic_str_view { + static_assert(!std::is_array::value, "CharT must not be an array type."); + static_assert( + std::is_trivial::value && std::is_standard_layout::value, + "CharT must be a trivial, standard layout type."); + static_assert( + std::is_same::value, "CharT & Traits::char_type must be the same type."); + +public: + /// Character traits type. + using traits_type = Traits; + /// Character type. + using value_type = CharT; + /// Pointer type to a character. + using pointer = value_type*; + /// Constant pointer type to a character. + using const_pointer = const value_type*; + /// Reference type to a character. + using reference = value_type&; + /// Constant reference type to a character. + using const_reference = const value_type&; + /// Constant iterator type to a character. + using const_iterator = const value_type*; + /// Iterator type to a character. + /// (Always constant since this class isn't meant to provide any mutating features.) + using iterator = const_iterator; + /// Constant reverse iterator type to a character. + using const_reverse_iterator = std::reverse_iterator; + /// Reverse iterator type to a character. + /// (Always constant since this class isn't meant to provide any mutating features.) + using reverse_iterator = const_reverse_iterator; + /// Size type for character sequence sizes. + using size_type = std::size_t; + /// Difference type for distances between characters. + using difference_type = std::ptrdiff_t; + + /// Invalid position value. + static constexpr size_type npos = static_cast(-1); + + /// Constructs a basic_str_view object. + basic_str_view() noexcept = default; + + /// Destroys a basic_str_view object. + ~basic_str_view() noexcept = default; + + /// @brief Copy constructs a basic_str_view object. + /// @param _ A basic_str_view object to copy from. + basic_str_view(const basic_str_view&) noexcept = default; + + /// @brief Move constructs a basic_str_view object. + /// @param _ A basic_str_view object to move from. + basic_str_view(basic_str_view&&) noexcept = default; + + /// @brief Constructs a basic_str_view object from a pointer to a character sequence. + /// @param p_str A pointer to a character sequence. (Must be null-terminated, or an undefined behavior.) + basic_str_view(const value_type* p_str) noexcept + : m_len(traits_type::length(p_str)), + mp_str(p_str) { + } + + /// @brief Construction from a null pointer is forbidden. + basic_str_view(std::nullptr_t) = delete; + + /// @brief Constructs a basic_str_view object from a poineter to a character sequence and its size. + /// @param p_str A pointer to a character sequence. (May or may not be null-terminated.) + /// @param len The length of a character sequence. + basic_str_view(const value_type* p_str, size_type len) noexcept + : m_len(len), + mp_str(p_str) { + } + + /// @brief Constructs a basic_str_view object from compatible begin/end iterators + /// @tparam ItrType Iterator type to a character. + /// @param first The iterator to the first element of a character sequence. + /// @param last The iterator to the past-the-end of a character sequence. + template < + typename ItrType, + enable_if_t< + conjunction< + is_iterator_of, + std::is_base_of< + std::random_access_iterator_tag, typename std::iterator_traits::iterator_category>>::value, + int> = 0> + basic_str_view(ItrType first, ItrType last) noexcept + : m_len(last - first), + mp_str(m_len > 0 ? &*first : nullptr) { + } + + /// @brief Constructs a basic_str_view object from a compatible std::basic_string object. + /// @param str A compatible character sequence container. + basic_str_view(const std::basic_string& str) noexcept + : m_len(str.length()), + mp_str(str.data()) { + } + + /// @brief Copy assignment operator for this basic_str_view class. + /// @param _ A basic_str_view object to copy from. + /// @return Reference to this basic_str_view object. + basic_str_view& operator=(const basic_str_view&) noexcept = default; + + /// @brief Move assignment operator for this basic_str_view class. + /// @param _ A basic_str_view object to move from. + /// @return Reference to this basic_str_view object. + basic_str_view& operator=(basic_str_view&&) noexcept = default; + + /// @brief Get the iterator to the first element. (Always constant) + /// @return The iterator to the first element. + const_iterator begin() const noexcept { + return mp_str; + } + + /// @brief Get the iterator to the past-the-end element. (Always constant) + /// @return The iterator to the past-the-end element. + const_iterator end() const noexcept { + return mp_str + m_len; + } + + /// @brief Get the iterator to the first element. (Always constant) + /// @return The iterator to the first element. + const_iterator cbegin() const noexcept { + return mp_str; + } + + /// @brief Get the iterator to the past-the-end element. (Always constant) + /// @return The iterator to the past-the-end element. + const_iterator cend() const noexcept { + return mp_str + m_len; + } + + /// @brief Get the iterator to the first element in the reverse order. (Always constant) + /// @return The iterator to the first element in the reverse order. + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant) + /// @return The iterator to the past-the-end element in the reverse order. + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + /// @brief Get the iterator to the first element in the reverse order. (Always constant) + /// @return The iterator to the first element in the reverse order. + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant) + /// @return The iterator to the past-the-end element in the reverse order. + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(begin()); + } + + /// @brief Get the size of the referenced character sequence. + /// @return The size of the referenced character sequence. + size_type size() const noexcept { + return m_len; + } + + /// @brief Get the size of the referenced character sequence. + /// @return The size of the referenced character sequence. + size_type length() const noexcept { + return m_len; + } + + /// @brief Get the maximum number of the character sequence size. + /// @return The maximum number of the character sequence size. + constexpr size_type max_size() const noexcept { + return static_cast(std::numeric_limits::max()); + } + + /// @brief Checks if the referenced character sequence is empty. + /// @return true if empty, false otherwise. + bool empty() const noexcept { + return m_len == 0; + } + + /// @brief Get the element at the given position. + /// @param pos The position of the target element. + /// @return The element at the given position. + const_reference operator[](size_type pos) const noexcept { + return *(mp_str + pos); + } + + /// @brief Get the element at the given position with bounds checks. + /// @warning Throws an fkyaml::out_of_range exception if the position exceeds the character sequence size. + /// @param pos The position of the target element. + /// @return The element at the given position. + const_reference at(size_type pos) const { + if FK_YAML_UNLIKELY (pos >= m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + return *(mp_str + pos); + } + + /// @brief Get the first element. + /// @return The first element. + const_reference front() const noexcept { + return *mp_str; + } + + /// @brief Get the last element. + /// @return The last element. + const_reference back() const { + return *(mp_str + m_len - 1); + } + + /// @brief Get the pointer to the raw data of referenced character sequence. + /// @return The pointer to the raw data of referenced character sequence. + const_pointer data() const noexcept { + return mp_str; + } + + /// @brief Moves the beginning position by `n` elements. + /// @param n The number of elements by which to move the beginning position. + void remove_prefix(size_type n) noexcept { + mp_str += n; + m_len -= n; + } + + /// @brief Shrinks the referenced character sequence from the last by `n` elements. + /// @param n The number of elements by which to shrink the sequence from the last. + void remove_suffix(size_type n) noexcept { + m_len -= n; + } + + /// @brief Swaps data with the given basic_str_view object. + /// @param other A basic_str_view object to swap data with. + void swap(basic_str_view& other) noexcept { + auto tmp = *this; + *this = other; + other = tmp; + } + + /// @brief Copys the referenced character sequence values from `pos` by `n` size. + /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the lenth. + /// @param p_str The pointer to a character sequence buffer for output. + /// @param n The number of elements to write into `p_str`. + /// @param pos The offset of the beginning position to copy values. + /// @return The number of elements to be written into `p_str`. + size_type copy(CharT* p_str, size_type n, size_type pos = 0) const { + if FK_YAML_UNLIKELY (pos > m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + const size_type rlen = std::min(n, m_len - pos); + traits_type::copy(p_str, mp_str + pos, rlen); + return rlen; + } + + /// @brief Constructs a sub basic_str_view object from `pos` by `n` size. + /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the lenth. + /// @param pos The offset of the beginning position. + /// @param n The number of elements to the end of a new sub basic_str_view object. + /// @return A newly created sub basic_str_view object. + basic_str_view substr(size_type pos = 0, size_type n = npos) const { + if FK_YAML_UNLIKELY (pos > m_len) { + throw fkyaml::out_of_range(static_cast(pos)); + } + const size_type rlen = std::min(n, m_len - pos); + return basic_str_view(mp_str + pos, rlen); + } + + /// @brief Compares the referenced character sequence values with the given basic_str_view object. + /// @param sv The basic_str_view object to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(basic_str_view sv) const noexcept { + const size_type rlen = std::min(m_len, sv.m_len); + int ret = traits_type::compare(mp_str, sv.mp_str, rlen); + + if (ret == 0) { + using int_limits = std::numeric_limits; + difference_type diff = + m_len > sv.m_len ? m_len - sv.m_len : difference_type(-1) * difference_type(sv.m_len - m_len); + + if (diff > int_limits::max()) { + ret = int_limits::max(); + } + else if (diff < int_limits::min()) { + ret = int_limits::min(); + } + else { + ret = static_cast(diff); + } + } + + return ret; + } + + /// @brief Compares the referenced character sequence values from `pos1` by `n1` characters with `sv`. + /// @param pos1 The offset of the beginning element. + /// @param n1 The length of character sequence used for comparison. + /// @param sv A basic_str_view object to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, basic_str_view sv) const { + return substr(pos1, n1).compare(sv); + } + + /// @brief Compares the referenced character sequence value from `pos1` by `n1` characters with `sv` from `pos2` by + /// `n2` characters. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used for comparison. + /// @param sv A basic_str_view object to compare with. + /// @param pos2 The offset of the beginning element in `sv`. + /// @param n2 The length of `sv` used for comparison. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, basic_str_view sv, size_type pos2, size_type n2) const { + return substr(pos1, n1).compare(sv.substr(pos2, n2)); + } + + /// @brief Compares the referenced character sequence with `s` character sequence. + /// @param s The pointer to a character sequence to compare with. + /// @return The lexicolographical comparison result. The values are same as std::strncmp(). + int compare(const CharT* s) const { + return compare(basic_str_view(s)); + } + + /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used fo comparison. + /// @param s The pointer to a character sequence to compare with. + /// @return The lexicographical comparison result. The values are same as std::strncmp(). + int compare(size_type pos1, size_type n1, const CharT* s) const { + return substr(pos1, n1).compare(basic_str_view(s)); + } + + /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence by + /// `n2` characters. + /// @param pos1 The offset of the beginning element in this character sequence. + /// @param n1 The length of this character sequence used fo comparison. + /// @param s The pointer to a character sequence to compare with. + /// @param n2 The length of `s` used fo comparison. + /// @return + int compare(size_type pos1, size_type n1, const CharT* s, size_type n2) const { + return substr(pos1, n1).compare(basic_str_view(s, n2)); + } + + /// @brief Checks if this character sequence starts with `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence starts with `sv` characters, false otherwise. + bool starts_with(basic_str_view sv) const noexcept { + return substr(0, sv.size()) == sv; + } + + /// @brief Checks if this character sequence starts with `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence starts with `c` character, false otherwise. + bool starts_with(CharT c) const noexcept { + return !empty() && traits_type::eq(front(), c); + } + + /// @brief Checks if this character sequence starts with `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence starts with `s` characters, false otherwise. + bool starts_with(const CharT* s) const noexcept { + return starts_with(basic_str_view(s)); + } + + /// @brief Checks if this character sequence ends with `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence ends with `sv` characters, false otherwise. + bool ends_with(basic_str_view sv) const noexcept { + const size_type size = m_len; + const size_type sv_size = sv.size(); + return size >= sv_size && traits_type::compare(end() - sv_size, sv.data(), sv_size) == 0; + } -// #include + /// @brief Checks if this character sequence ends with `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence ends with `c` character, false otherwise. + bool ends_with(CharT c) const noexcept { + return !empty() && traits_type::eq(back(), c); + } + /// @brief Checks if this character sequence ends with `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence ends with `s` characters, false otherwise. + bool ends_with(const CharT* s) const noexcept { + return ends_with(basic_str_view(s)); + } -FK_YAML_DETAIL_NAMESPACE_BEGIN + /// @brief Checks if this character sequence contains `sv` characters. + /// @param sv The character sequence to compare with. + /// @return true if the character sequence contains `sv` characters, false otherwise. + bool contains(basic_str_view sv) const noexcept { + return find(sv) != npos; + } -/// @brief Definition of lexical token types. -enum class lexical_token_t { - END_OF_BUFFER, //!< the end of input buffer. - EXPLICIT_KEY_PREFIX, //!< the character for explicit mapping key prefix `?`. - KEY_SEPARATOR, //!< the key separater `:` - VALUE_SEPARATOR, //!< the value separater `,` - ANCHOR_PREFIX, //!< the character for anchor prefix `&` - ALIAS_PREFIX, //!< the character for alias prefix `*` - YAML_VER_DIRECTIVE, //!< a YAML version directive found. use get_yaml_version() to get a value. - TAG_DIRECTIVE, //!< a TAG directive found. use GetTagInfo() to get the tag information. - TAG_PREFIX, //!< the character for tag prefix `!` - INVALID_DIRECTIVE, //!< an invalid directive found. do not try to get the value. - SEQUENCE_BLOCK_PREFIX, //!< the character for sequence block prefix `- ` - SEQUENCE_FLOW_BEGIN, //!< the character for sequence flow begin `[` - SEQUENCE_FLOW_END, //!< the character for sequence flow end `]` - MAPPING_FLOW_BEGIN, //!< the character for mapping begin `{` - MAPPING_FLOW_END, //!< the character for mapping end `}` - NULL_VALUE, //!< a null value found. use get_null() to get a value. - BOOLEAN_VALUE, //!< a boolean value found. use get_boolean() to get a value. - INTEGER_VALUE, //!< an integer value found. use get_integer() to get a value. - FLOAT_NUMBER_VALUE, //!< a float number value found. use get_float_number() to get a value. - STRING_VALUE, //!< the character for string begin `"` or any character except the above ones - END_OF_DIRECTIVES, //!< the end of declaration of directives specified by `---`. - END_OF_DOCUMENT, //!< the end of a YAML document specified by `...`. -}; + /// @brief Checks if this character sequence contains `c` character. + /// @param c The character to compare with. + /// @return true if the character sequence contains `c` character, false otherwise. + bool contains(CharT c) const noexcept { + return find(c) != npos; + } -FK_YAML_DETAIL_NAMESPACE_END + /// @brief Checks if this character sequence contains `s` characters. + /// @param s The character sequence to compare with. + /// @return true if the character sequence contains `s` characters, false otherwise. + bool contains(const CharT* s) const noexcept { + return find(s) != npos; + } -#endif /* FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ */ + /// @brief Finds the beginning position of `sv` characters in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find(basic_str_view sv, size_type pos = 0) const noexcept { + return find(sv.mp_str, pos, sv.m_len); + } + /// @brief Finds the beginning position of `c` character in this referenced character sequence. + /// @param sv The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find(CharT c, size_type pos = 0) const noexcept { + size_type ret = npos; -FK_YAML_DETAIL_NAMESPACE_BEGIN + if FK_YAML_LIKELY (pos < m_len) { + const size_type n = m_len - pos; + const CharT* p_found = traits_type::find(mp_str + pos, n, c); + if (p_found) { + ret = p_found - mp_str; + } + } -namespace { + return ret; + } -/// @brief Check if the given character is a digit. -/// @note This function is needed to avoid assertion failures in `std::isdigit()` especially when compiled with MSVC. -/// @param c A character to be checked. -/// @return true if the given character is a digit, false otherwise. -inline bool is_digit(char c) { - return ('0' <= c && c <= '9'); -} + /// @brief Finds the beginning position of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n == 0) { + return pos <= m_len ? pos : npos; + } -/// @brief Check if the given character is a hex-digit. -/// @note This function is needed to avoid assertion failures in `std::isxdigit()` especially when compiled with MSVC. -/// @param c A character to be checked. -/// @return true if the given character is a hex-digit, false otherwise. -inline bool is_xdigit(char c) { - return (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')); -} + if FK_YAML_UNLIKELY (pos >= m_len) { + return npos; + } -} // namespace -class scalar_scanner { -public: - static lexical_token_t scan(const std::string& token) { - switch (token.size()) { - case 0: - return lexical_token_t::STRING_VALUE; - case 1: - if (token[0] == '~') { - return lexical_token_t::NULL_VALUE; - } - break; - case 4: - switch (token[0]) { - case 'n': - case 'N': - if (token == "null" || token == "Null" || token == "NULL") { - return lexical_token_t::NULL_VALUE; - } - break; - case 't': - case 'T': - if (token == "true" || token == "True" || token == "TRUE") { - return lexical_token_t::BOOLEAN_VALUE; - } - break; - case '.': - if (token == ".inf" || token == ".Inf" || token == ".INF" || token == ".nan" || token == ".NaN" || - token == ".NAN") { - return lexical_token_t::FLOAT_NUMBER_VALUE; - } - break; + CharT s0 = s[0]; + const CharT* p_first = mp_str + pos; + const CharT* p_last = mp_str + m_len; + size_type len = m_len - pos; + + while (len >= n) { + // find the first occurence of s0 + p_first = traits_type::find(p_first, len - n + 1, s0); + if (!p_first) { + return npos; } - break; - case 5: - switch (token[0]) { - case 'f': - case 'F': - if (token == "false" || token == "False" || token == "FALSE") { - return lexical_token_t::BOOLEAN_VALUE; - } - break; - case '-': - if (token[1] == '.' && (token == "-.inf" || token == "-.Inf" || token == "-.INF")) { - return lexical_token_t::FLOAT_NUMBER_VALUE; - } - break; + + // compare the full strings from the first occurence of s0 + if (traits_type::compare(p_first, s, n) == 0) { + return p_first - mp_str; } - break; + + len = p_last - (++p_first); } - return scan_possible_number_token(token); + return npos; } -private: - static lexical_token_t scan_possible_number_token(const std::string& token) { - std::string::const_iterator itr = token.begin(); - std::size_t size = token.size(); - FK_YAML_ASSERT(size > 0); + /// @brief Finds the beginning position of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find(const CharT* s, size_type pos = 0) const noexcept { + return find(basic_str_view(s), pos); + } - switch (*itr) { - case '-': - return (size > 1) ? scan_negative_number(++itr, --size) : lexical_token_t::STRING_VALUE; - case '+': - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::STRING_VALUE; - case '0': - return (size > 1) ? scan_after_zero_at_first(++itr, --size) : lexical_token_t::INTEGER_VALUE; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::INTEGER_VALUE; - default: - return lexical_token_t::STRING_VALUE; - } + /// @brief Retrospectively finds the beginning position of `sv` characters in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type rfind(basic_str_view sv, size_type pos = npos) const noexcept { + return rfind(sv.mp_str, pos, sv.m_len); } - static lexical_token_t scan_negative_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Retrospectively finds the beginning position of `c` character in this referenced character sequence. + /// @param sv The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type rfind(CharT c, size_type pos = npos) const noexcept { + if FK_YAML_UNLIKELY (m_len == 0) { + return npos; + } - if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, false) : lexical_token_t::INTEGER_VALUE; + size_type idx = pos; + if (pos >= m_len) { + idx = m_len - 1; } - return lexical_token_t::STRING_VALUE; + do { + if (traits_type::eq(mp_str[idx], c)) { + return idx; + } + } while (idx > 0 && --idx < m_len); + + return npos; } - static lexical_token_t scan_after_zero_at_first(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Retrospectively finds the beginning position of `s` character sequence by `n` characters in this + /// referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type rfind(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_LIKELY (n <= m_len) { + pos = std::min(m_len - n, pos) + 1; - if (is_digit(*itr)) { - // a token consisting of the beginning '0' and some following numbers, e.g., `0123`, is not an integer - // according to https://yaml.org/spec/1.2.2/#10213-integer. - return lexical_token_t::STRING_VALUE; + do { + if (traits_type::compare(mp_str + --pos, s, n) == 0) { + return pos; + } + } while (pos > 0); } - switch (*itr) { - case '.': { - if (size == 1) { - // 0 is omitted after `0.`. - return lexical_token_t::FLOAT_NUMBER_VALUE; - } - lexical_token_t ret = scan_after_decimal_point(++itr, --size, true); - return (ret == lexical_token_t::STRING_VALUE) ? lexical_token_t::STRING_VALUE - : lexical_token_t::FLOAT_NUMBER_VALUE; - } - case 'o': - return (size > 1) ? scan_octal_number(++itr, --size) : lexical_token_t::STRING_VALUE; - case 'x': - return (size > 1) ? scan_hexadecimal_number(++itr, --size) : lexical_token_t::STRING_VALUE; - default: - return lexical_token_t::STRING_VALUE; - } + return npos; } - static lexical_token_t scan_decimal_number( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Retrospectively finds the beginning position of `s` character sequence in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type rfind(const CharT* s, size_type pos = npos) const noexcept { + return rfind(basic_str_view(s), pos); + } - if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) : lexical_token_t::INTEGER_VALUE; + /// @brief Finds the first occurence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find_first_of(basic_str_view sv, size_type pos = 0) const noexcept { + return find_first_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the first occurence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find_first_of(CharT c, size_type pos = 0) const noexcept { + return find(c, pos); + } + + /// @brief Finds the first occurence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_first_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n == 0) { + return npos; } - switch (*itr) { - case '.': { - if (has_decimal_point) { - // the token has more than one period, e.g., a semantic version `1.2.3`. - return lexical_token_t::STRING_VALUE; + for (size_type idx = pos; idx < m_len; ++idx) { + const CharT* p_found = traits_type::find(s, n, mp_str[idx]); + if (p_found) { + return idx; } - if (size == 1) { - // 0 is omitted after the decimal point - return lexical_token_t::FLOAT_NUMBER_VALUE; - } - lexical_token_t ret = scan_after_decimal_point(++itr, --size, true); - return (ret == lexical_token_t::STRING_VALUE) ? lexical_token_t::STRING_VALUE - : lexical_token_t::FLOAT_NUMBER_VALUE; - } - case 'e': - case 'E': - return (size > 1) ? scan_after_exponent(++itr, --size, has_decimal_point) : lexical_token_t::STRING_VALUE; - default: - return lexical_token_t::STRING_VALUE; } + + return npos; + } + + /// @brief Finds the first occurence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_first_of(const CharT* s, size_type pos = 0) const noexcept { + return find_first_of(basic_str_view(s), pos); } - static lexical_token_t scan_after_decimal_point( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Finds the last occurence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `sv` characters, `npos` otherwise. + size_type find_last_of(basic_str_view sv, size_type pos = npos) const noexcept { + return find_last_of(sv.mp_str, pos, sv.m_len); + } - if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) - : lexical_token_t::FLOAT_NUMBER_VALUE; + /// @brief Finds the last occurence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `c` character, `npos` otherwise. + size_type find_last_of(CharT c, size_type pos = npos) const noexcept { + return rfind(c, pos); + } + + /// @brief Finds the last occurence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_last_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_LIKELY (n <= m_len) { + pos = std::min(m_len - n - 1, pos); + + do { + const CharT* p_found = traits_type::find(s, n, mp_str[pos]); + if (p_found) { + return pos; + } + } while (pos-- != 0); } - return lexical_token_t::STRING_VALUE; + return npos; } - static lexical_token_t scan_after_exponent( - std::string::const_iterator itr, std::size_t size, bool has_decimal_point) { - FK_YAML_ASSERT(size > 0); + /// @brief Finds the last occurence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of `s` characters, `npos` otherwise. + size_type find_last_of(const CharT* s, size_type pos = npos) const noexcept { + return find_last_of(basic_str_view(s), pos); + } - if (is_digit(*itr)) { - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) - : lexical_token_t::FLOAT_NUMBER_VALUE; + /// @brief Finds the first absence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `sv` characters, `npos` otherwise. + size_type find_first_not_of(basic_str_view sv, size_type pos = 0) const noexcept { + return find_first_not_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the first absence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `c` character, `npos` otherwise. + size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept { + for (; pos < m_len; ++pos) { + if (!traits_type::eq(mp_str[pos], c)) { + return pos; + } } - switch (*itr) { - case '+': - case '-': - return (size > 1) ? scan_decimal_number(++itr, --size, has_decimal_point) : lexical_token_t::STRING_VALUE; - default: - return lexical_token_t::STRING_VALUE; + return npos; + } + + /// @brief Finds the first absence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const noexcept { + for (; pos < m_len; ++pos) { + const CharT* p_found = traits_type::find(s, n, mp_str[pos]); + if (!p_found) { + return pos; + } } + + return npos; } - static lexical_token_t scan_octal_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Finds the first absence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_first_not_of(const CharT* s, size_type pos = 0) const noexcept { + return find_first_not_of(basic_str_view(s), pos); + } - switch (*itr) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - return (size > 1) ? scan_octal_number(++itr, --size) : lexical_token_t::INTEGER_VALUE; - default: - return lexical_token_t::STRING_VALUE; + /// @brief Finds the last absence of `sv` character sequence in this referenced character sequence. + /// @param sv The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `sv` characters, `npos` otherwise. + size_type find_last_not_of(basic_str_view sv, size_type pos = npos) const noexcept { + return find_last_not_of(sv.mp_str, pos, sv.m_len); + } + + /// @brief Finds the last absence of `c` character in this referenced character sequence. + /// @param c The character to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `c` character, `npos` otherwise. + size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept { + if FK_YAML_LIKELY (m_len > 0) { + pos = std::min(m_len, pos); + + do { + if (!traits_type::eq(mp_str[--pos], c)) { + return pos; + } + } while (pos > 0); } + + return npos; } - static lexical_token_t scan_hexadecimal_number(std::string::const_iterator itr, std::size_t size) { - FK_YAML_ASSERT(size > 0); + /// @brief Finds the last absence of `s` character sequence by `n` characters in this referenced character + /// sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @param n The length of `s` character sequence used for comparison. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const noexcept { + if FK_YAML_UNLIKELY (n <= m_len) { + pos = std::min(m_len - n, pos) + 1; - if (is_xdigit(*itr)) { - return (size > 1) ? scan_hexadecimal_number(++itr, --size) : lexical_token_t::INTEGER_VALUE; + do { + const CharT* p_found = traits_type::find(s, n, mp_str[--pos]); + if (!p_found) { + return pos; + } + } while (pos > 0); } - return lexical_token_t::STRING_VALUE; + + return npos; } -}; -FK_YAML_DETAIL_NAMESPACE_END + /// @brief Finds the last absence of `s` character sequence in this referenced character sequence. + /// @param s The character sequence to compare with. + /// @param pos The offset of the search beginning position in this referenced character sequence. + /// @return The beginning position of non `s` characters, `npos` otherwise. + size_type find_last_not_of(const CharT* s, size_type pos = npos) const noexcept { + return find_last_not_of(basic_str_view(s), pos); + } -#endif /* FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ */ +private: + size_type m_len {0}; + const value_type* mp_str {nullptr}; +}; -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file +// Prior to C++17, a static constexpr class member needs an out-of-class definition. +#ifndef FK_YAML_HAS_CXX_17 -#ifndef FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ -#define FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ +template +constexpr typename basic_str_view::size_type basic_str_view::npos; + +#endif // !defined(FK_YAML_HAS_CXX_17) + +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, basic_str_view rhs) noexcept { + // Comparing the lengths first will omit unnecessary value comparison in compare(). + return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; +} -#include -#include -#include -#include +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_string object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, const std::basic_string& rhs) noexcept { + return lhs == basic_str_view(rhs); +} -// #include +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_string object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(const std::basic_string& lhs, basic_str_view rhs) noexcept { + return basic_str_view(lhs) == rhs; +} -// #include -/// _______ __ __ __ _____ __ __ __ -/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -/// -/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -/// SPDX-License-Identifier: MIT -/// -/// @file +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A character array to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(basic_str_view lhs, const CharT (&rhs)[N]) noexcept { + // assume `rhs` is null terminated + return lhs == basic_str_view(rhs, N - 1); +} -#ifndef FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ -#define FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ +/// @brief An equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param rhs A character array for comparison. +/// @param lhs A basic_str_view object to compare with. +/// @return true if the two objects are the same, false otherwise. +template +inline bool operator==(const CharT (&lhs)[N], basic_str_view rhs) noexcept { + // assume `lhs` is null terminated + return basic_str_view(lhs, N - 1) == rhs; +} -#include -#include +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, basic_str_view rhs) noexcept { + return !(lhs == rhs); +} -// #include +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_string object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, const std::basic_string& rhs) noexcept { + return !(lhs == basic_str_view(rhs)); +} -// #include +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_string object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(const std::basic_string& lhs, basic_str_view rhs) noexcept { + return !(basic_str_view(lhs) == rhs); +} -// #include +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A character array to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(basic_str_view lhs, const CharT (&rhs)[N]) noexcept { + // assume `rhs` is null terminated. + return !(lhs == basic_str_view(rhs, N - 1)); +} +/// @brief An not-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @tparam N The length of the character array. +/// @param rhs A character array for comparison. +/// @param lhs A basic_str_view object to compare with. +/// @return true if the two objects are different, false otherwise. +template +inline bool operator!=(const CharT (&lhs)[N], basic_str_view rhs) noexcept { + // assume `lhs` is null terminate + return !(basic_str_view(lhs, N - 1) == rhs); +} -FK_YAML_DETAIL_NAMESPACE_BEGIN +/// @brief An less-than operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is less than `rhs`, false otherwise. +template +inline bool operator<(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) < 0; +} -/////////////////////////////////////////// -// Input Adapter API detection traits -/////////////////////////////////////////// +/// @brief An less-than-or-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is less than or equal to `rhs`, false otherwise. +template +inline bool operator<=(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) <= 0; +} -/// @brief A type which represents get_character function. -/// @tparam T A target type. -template -using fill_buffer_fn_t = decltype(std::declval().fill_buffer(std::declval())); +/// @brief An greater-than operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is greater than `rhs`, false otherwise. +template +inline bool operator>(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) > 0; +} -/// @brief Type traits to check if InputAdapterType has get_character member function. -/// @tparam InputAdapterType An input adapter type to check if it has get_character function. -/// @tparam typename N/A -template -struct has_fill_buffer : std::false_type {}; +/// @brief An greater-than-or-equal-to operator of the basic_str_view class. +/// @tparam CharT Character type +/// @tparam Traits Character traits type. +/// @param lhs A basic_str_view object for comparison. +/// @param rhs A basic_str_view object to compare with. +/// @return true if `lhs` is greater than or equal to `rhs`, false otherwise. +template +inline bool operator>=(basic_str_view lhs, basic_str_view rhs) noexcept { + return lhs.compare(rhs) >= 0; +} -/// @brief A partial specialization of has_fill_buffer if InputAdapterType has get_character member function. -/// @tparam InputAdapterType A type of a target input adapter. -template -struct has_fill_buffer::value>> - : std::true_type {}; +/// @brief Insertion operator of the basic_str_view class. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +/// @param os An output stream object. +/// @param sv A basic_str_view object. +/// @return Reference to the output stream object `os`. +template +inline std::basic_ostream& operator<<( + std::basic_ostream& os, basic_str_view sv) { + return os.write(sv.data(), static_cast(sv.size())); +} -//////////////////////////////// -// is_input_adapter traits -//////////////////////////////// +/// @brief view into `char` sequence. +using str_view = basic_str_view; -/// @brief Type traits to check if T is an input adapter type. -/// @tparam T A target type. -/// @tparam typename N/A -template -struct is_input_adapter : std::false_type {}; +#if FK_YAML_HAS_CHAR8_T +/// @brief view into `char8_t` sequence. +using u8str_view = basic_str_view; +#endif -/// @brief A partial specialization of is_input_adapter if T is an input adapter type. -/// @tparam InputAdapterType -template -struct is_input_adapter::value>> : std::true_type {}; +/// @brief view into `char16_t` sequence. +using u16str_view = basic_str_view; -FK_YAML_DETAIL_NAMESPACE_END +/// @brief view into `char32_t` sequence. +using u32str_view = basic_str_view; -#endif /* FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ */ +FK_YAML_DETAIL_NAMESPACE_END -// #include +#endif /* FK_YAML_DETAIL_STR_VIEW_HPP_ */ FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -2585,7 +3961,7 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @brief A position tracker of the target buffer. class position_tracker { public: - void set_target_buffer(const std::string& buffer) { + void set_target_buffer(str_view buffer) noexcept { m_begin = m_last = buffer.begin(); m_end = buffer.end(); } @@ -2593,19 +3969,26 @@ class position_tracker { /// @brief Update the set of the current position informations. /// @note This function doesn't support cases where cur_pos has moved backward from the last call. /// @param cur_pos The iterator to the current element of the buffer. - void update_position(std::string::const_iterator cur_pos) { - m_cur_pos = static_cast(std::distance(m_begin, cur_pos)); - m_lines_read += static_cast(std::count(m_last, cur_pos, '\n')); - m_last = cur_pos; + void update_position(const char* p_current) { + uint32_t diff = static_cast(p_current - m_last); + if (diff == 0) { + return; + } - if (m_lines_read == 0) { - m_cur_pos_in_line = m_cur_pos; + m_cur_pos += diff; + uint32_t prev_lines_read = m_lines_read; + m_lines_read += static_cast(std::count(m_last, p_current, '\n')); + m_last = p_current; + + if (prev_lines_read == m_lines_read) { + m_cur_pos_in_line += diff; return; } uint32_t count = 0; - while (--cur_pos != m_begin) { - if (*cur_pos == '\n') { + const char* p_begin = m_begin; + while (--p_current != p_begin) { + if (*p_current == '\n') { break; } count++; @@ -2631,11 +4014,11 @@ class position_tracker { private: /// The iterator to the beginning element in the target buffer. - std::string::const_iterator m_begin {}; + const char* m_begin {}; /// The iterator to the past-the-end element in the target buffer. - std::string::const_iterator m_end {}; + const char* m_end {}; /// The iterator to the last updated element in the target buffer. - std::string::const_iterator m_last {}; + const char* m_last {}; /// The current position from the beginning of an input buffer. uint32_t m_cur_pos {0}; /// The current position in the current line. @@ -2648,26 +4031,71 @@ FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP_ */ -// #include - -// #include - // #include +// #include + // #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ +#define FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Definition of lexical token types. +enum class lexical_token_t { + END_OF_BUFFER, //!< the end of input buffer. + EXPLICIT_KEY_PREFIX, //!< the character for explicit mapping key prefix `?`. + KEY_SEPARATOR, //!< the key separater `:` + VALUE_SEPARATOR, //!< the value separater `,` + ANCHOR_PREFIX, //!< the character for anchor prefix `&` + ALIAS_PREFIX, //!< the character for alias prefix `*` + YAML_VER_DIRECTIVE, //!< a YAML version directive found. use get_yaml_version() to get a value. + TAG_DIRECTIVE, //!< a TAG directive found. use GetTagInfo() to get the tag information. + TAG_PREFIX, //!< the character for tag prefix `!` + INVALID_DIRECTIVE, //!< an invalid directive found. do not try to get the value. + SEQUENCE_BLOCK_PREFIX, //!< the character for sequence block prefix `- ` + SEQUENCE_FLOW_BEGIN, //!< the character for sequence flow begin `[` + SEQUENCE_FLOW_END, //!< the character for sequence flow end `]` + MAPPING_FLOW_BEGIN, //!< the character for mapping begin `{` + MAPPING_FLOW_END, //!< the character for mapping end `}` + PLAIN_SCALAR, //!< plain (unquoted) scalars + SINGLE_QUOTED_SCALAR, //!< single-quoted scalars + DOUBLE_QUOTED_SCALAR, //!< double-quoted scalars + BLOCK_SCALAR, //!< block style scalars + END_OF_DIRECTIVES, //!< the end of declaration of directives specified by `---`. + END_OF_DOCUMENT, //!< the end of a YAML document specified by `...`. +}; + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP_ */ // #include FK_YAML_DETAIL_NAMESPACE_BEGIN +struct lexical_token { + lexical_token_t type {lexical_token_t::END_OF_BUFFER}; + str_view str {}; +}; + /// @brief A class which lexically analizes YAML formatted inputs. -/// @tparam BasicNodeType A type of the container for YAML values. -template ::value, int> = 0> class lexical_analyzer { private: - using char_traits_type = typename std::char_traits; - enum class block_style_indicator_t { LITERAL, //!< keeps newlines inside the block as they are indicated by a pipe `|`. FOLDED, //!< replaces newlines inside the block with spaces indicated by a right angle bracket `>`. @@ -2680,25 +4108,19 @@ class lexical_analyzer { }; public: - using boolean_type = typename BasicNodeType::boolean_type; - using integer_type = typename BasicNodeType::integer_type; - using float_number_type = typename BasicNodeType::float_number_type; - using string_type = typename BasicNodeType::string_type; - /// @brief Construct a new lexical_analyzer object. /// @tparam InputAdapterType The type of the input adapter. /// @param input_adapter An input adapter object. - template ::value, int> = 0> - explicit lexical_analyzer(InputAdapterType&& input_adapter) { - std::forward(input_adapter).fill_buffer(m_input_buffer); - m_cur_itr = m_token_begin_itr = m_input_buffer.cbegin(); - m_end_itr = m_input_buffer.cend(); + explicit lexical_analyzer(str_view input_buffer) noexcept + : m_input_buffer(input_buffer), + m_cur_itr(m_input_buffer.begin()), + m_end_itr(m_input_buffer.end()) { m_pos_tracker.set_target_buffer(m_input_buffer); } - /// @brief Get the next lexical token type by scanning the left of the input buffer. - /// @return lexical_token_t The next lexical token type. - lexical_token_t get_next_token() { + /// @brief Get the next lexical token by scanning the left of the input buffer. + /// @return lexical_token The next lexical token. + lexical_token get_next_token() { skip_white_spaces_and_newline_codes(); m_token_begin_itr = m_cur_itr; @@ -2707,25 +4129,31 @@ class lexical_analyzer { m_last_token_begin_line = m_pos_tracker.get_lines_read(); if (m_cur_itr == m_end_itr) { - return lexical_token_t::END_OF_BUFFER; + return {}; } - switch (char current = *m_cur_itr) { + lexical_token token {}; + token.type = lexical_token_t::PLAIN_SCALAR; + + switch (*m_cur_itr) { case '?': if (++m_cur_itr == m_end_itr) { - m_value_buffer = "?"; - return lexical_token_t::STRING_VALUE; + token.str = str_view {m_token_begin_itr, m_end_itr}; + return token; } switch (*m_cur_itr) { case ' ': - return lexical_token_t::EXPLICIT_KEY_PREFIX; + token.type = lexical_token_t::EXPLICIT_KEY_PREFIX; + return token; default: - return scan_scalar(); + scan_scalar(token); + return token; } case ':': { // key separater if (++m_cur_itr == m_end_itr) { - return lexical_token_t::KEY_SEPARATOR; + token.type = lexical_token_t::KEY_SEPARATOR; + return token; } switch (*m_cur_itr) { @@ -2743,41 +4171,50 @@ class lexical_analyzer { // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. break; } - return scan_scalar(); + scan_scalar(token); + return token; default: - return scan_scalar(); + scan_scalar(token); + return token; } - return lexical_token_t::KEY_SEPARATOR; + token.type = lexical_token_t::KEY_SEPARATOR; + return token; } case ',': // value separater ++m_cur_itr; - return lexical_token_t::VALUE_SEPARATOR; + token.type = lexical_token_t::VALUE_SEPARATOR; + return token; case '&': { // anchor prefix - extract_anchor_name(); - bool is_empty = m_value_buffer.empty(); - if (is_empty) { + extract_anchor_name(token); + bool is_empty = token.str.empty(); + if FK_YAML_UNLIKELY (is_empty) { emit_error("anchor name must not be empty."); } - return lexical_token_t::ANCHOR_PREFIX; + + token.type = lexical_token_t::ANCHOR_PREFIX; + return token; } case '*': { // alias prefix - extract_anchor_name(); - bool is_empty = m_value_buffer.empty(); - if (is_empty) { + extract_anchor_name(token); + bool is_empty = token.str.empty(); + if FK_YAML_UNLIKELY (is_empty) { emit_error("anchor name must not be empty."); } - return lexical_token_t::ALIAS_PREFIX; + token.type = lexical_token_t::ALIAS_PREFIX; + return token; } case '!': - extract_tag_name(); - return lexical_token_t::TAG_PREFIX; + extract_tag_name(token); + token.type = lexical_token_t::TAG_PREFIX; + return token; case '#': // comment prefix scan_comment(); return get_next_token(); case '%': // directive prefix - return scan_directive(); + token.type = scan_directive(); + return token; case '-': { char next = *(m_cur_itr + 1); switch (next) { @@ -2786,71 +4223,93 @@ class lexical_analyzer { case '\n': // Move a cursor to the beginning of the next token. m_cur_itr += 2; - return lexical_token_t::SEQUENCE_BLOCK_PREFIX; + token.type = lexical_token_t::SEQUENCE_BLOCK_PREFIX; + return token; default: break; } - bool is_available = (std::distance(m_cur_itr, m_end_itr) > 2); + bool is_available = ((m_end_itr - m_cur_itr) > 2); if (is_available) { - if (std::equal(m_token_begin_itr, m_cur_itr + 3, "---")) { + bool is_dir_end = std::equal(m_token_begin_itr, m_cur_itr + 3, "---"); + if (is_dir_end) { m_cur_itr += 3; - return lexical_token_t::END_OF_DIRECTIVES; + token.type = lexical_token_t::END_OF_DIRECTIVES; + return token; } } - return scan_scalar(); + scan_scalar(token); + return token; } case '[': // sequence flow begin m_flow_context_depth++; ++m_cur_itr; - return lexical_token_t::SEQUENCE_FLOW_BEGIN; + token.type = lexical_token_t::SEQUENCE_FLOW_BEGIN; + return token; case ']': // sequence flow end - m_flow_context_depth--; + m_flow_context_depth = (m_flow_context_depth > 0) ? m_flow_context_depth - 1 : 0; ++m_cur_itr; - return lexical_token_t::SEQUENCE_FLOW_END; + token.type = lexical_token_t::SEQUENCE_FLOW_END; + return token; case '{': // mapping flow begin m_flow_context_depth++; ++m_cur_itr; - return lexical_token_t::MAPPING_FLOW_BEGIN; + token.type = lexical_token_t::MAPPING_FLOW_BEGIN; + return token; case '}': // mapping flow end - m_flow_context_depth--; + m_flow_context_depth = (m_flow_context_depth > 0) ? m_flow_context_depth - 1 : 0; ++m_cur_itr; - return lexical_token_t::MAPPING_FLOW_END; + token.type = lexical_token_t::MAPPING_FLOW_END; + return token; case '@': emit_error("Any token cannot start with at(@). It is a reserved indicator for YAML."); case '`': emit_error("Any token cannot start with grave accent(`). It is a reserved indicator for YAML."); case '\"': case '\'': - return scan_scalar(); + scan_scalar(token); + return token; case '+': - return scan_scalar(); + scan_scalar(token); + return token; case '.': { - bool is_available = (std::distance(m_cur_itr, m_end_itr) > 2); + bool is_available = ((m_end_itr - m_cur_itr) > 2); if (is_available) { - if (std::equal(m_cur_itr, m_cur_itr + 3, "...")) { + bool is_doc_end = std::equal(m_cur_itr, m_cur_itr + 3, "..."); + if (is_doc_end) { m_cur_itr += 3; - return lexical_token_t::END_OF_DOCUMENT; + token.type = lexical_token_t::END_OF_DOCUMENT; + return token; } } - return scan_scalar(); + scan_scalar(token); + return token; } case '|': { chomping_indicator_t chomp_type = chomping_indicator_t::KEEP; uint32_t indent = 0; + ++m_cur_itr; get_block_style_metadata(chomp_type, indent); - return scan_block_style_string_token(block_style_indicator_t::LITERAL, chomp_type, indent); + scan_block_style_string_token(block_style_indicator_t::LITERAL, chomp_type, indent); + token.type = lexical_token_t::BLOCK_SCALAR; + token.str = m_value_buffer; + return token; } case '>': { chomping_indicator_t chomp_type = chomping_indicator_t::KEEP; uint32_t indent = 0; + ++m_cur_itr; get_block_style_metadata(chomp_type, indent); - return scan_block_style_string_token(block_style_indicator_t::FOLDED, chomp_type, indent); + scan_block_style_string_token(block_style_indicator_t::FOLDED, chomp_type, indent); + token.type = lexical_token_t::BLOCK_SCALAR; + token.str = m_value_buffer; + return token; } default: - return scan_scalar(); + scan_scalar(token); + return token; } } @@ -2866,65 +4325,21 @@ class lexical_analyzer { return m_last_token_begin_line; } - /// @brief Convert from string to null and get the converted value. - /// @return std::nullptr_t A null value converted from one of the followings: "null", "Null", "NULL", "~". - std::nullptr_t get_null() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to boolean and get the converted value. - /// @retval true A string token is one of the followings: "true", "True", "TRUE". - /// @retval false A string token is one of the followings: "false", "False", "FALSE". - boolean_type get_boolean() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to integer and get the converted value. - /// @return integer_type An integer value converted from the source string. - integer_type get_integer() const { - if (m_value_buffer.size() > 2 && m_value_buffer.rfind("0o", 0) != std::string::npos) { - // Replace the prefix "0o" with "0" since STL functions can detect octal chars. - // Note that the YAML specifies octal values start with the prefix "0o", not "0". - // See https://yaml.org/spec/1.2.2/#1032-tag-resolution for more details. - return from_string("0" + m_value_buffer.substr(2), type_tag {}); - } - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Convert from string to float number and get the converted value. - /// @return float_number_type A float number value converted from the source string. - float_number_type get_float_number() const { - return from_string(m_value_buffer, type_tag {}); - } - - /// @brief Get a scanned string value. - /// @return const string_type& Constant reference to a scanned string. - const string_type& get_string() const noexcept { - // TODO: Provide support for different string types between nodes & inputs. - static_assert(std::is_same::value, "Unsupported, different string types."); - return m_value_buffer; - } - /// @brief Get the YAML version specification. - /// @return const string_type& A YAML version specification. - const string_type& get_yaml_version() const { - FK_YAML_ASSERT(!m_value_buffer.empty() && m_value_buffer.size() == 3); - FK_YAML_ASSERT(m_value_buffer == "1.1" || m_value_buffer == "1.2"); - - return m_value_buffer; + /// @return str_view A YAML version specification. + str_view get_yaml_version() const noexcept { + return m_yaml_version; } /// @brief Get the YAML tag handle defined in the TAG directive. - /// @return const std::string& A tag handle. - const std::string& get_tag_handle() const { - FK_YAML_ASSERT(!m_tag_handle.empty()); + /// @return str_view A tag handle. + str_view get_tag_handle() const noexcept { return m_tag_handle; } /// @brief Get the YAML tag prefix defined in the TAG directive. - /// @return const std::string A tag prefix. - const std::string& get_tag_prefix() const { - FK_YAML_ASSERT(!m_tag_prefix.empty()); + /// @return str_view A tag prefix. + str_view get_tag_prefix() const noexcept { return m_tag_prefix; } @@ -2960,19 +4375,19 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + str_view dir_name(m_token_begin_itr, m_cur_itr); - if (m_value_buffer == "TAG") { - if (!ends_loop) { + if (dir_name == "TAG") { + if FK_YAML_UNLIKELY (!ends_loop) { emit_error("There must be at least one white space between \"%TAG\" and tag info."); } skip_white_spaces(); return scan_tag_directive(); } - if (m_value_buffer == "YAML") { - if (!ends_loop) { - emit_error("There must be at least one white space between \"%YAML\" and tag info."); + if (dir_name == "YAML") { + if FK_YAML_UNLIKELY (!ends_loop) { + emit_error("There must be at least one white space between \"%YAML\" and version."); } skip_white_spaces(); return scan_yaml_version_directive(); @@ -2985,19 +4400,17 @@ class lexical_analyzer { /// @brief Scan a YAML tag directive. /// @return lexical_token_t The lexical token type for YAML tag directives. lexical_token_t scan_tag_directive() { - m_tag_handle.clear(); - m_tag_prefix.clear(); m_token_begin_itr = m_cur_itr; // // extract a tag handle // - if (*m_cur_itr != '!') { + if FK_YAML_UNLIKELY (*m_cur_itr != '!') { emit_error("Tag handle must start with \'!\'."); } - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } @@ -3007,10 +4420,10 @@ class lexical_analyzer { // primary handle (!) break; case '!': - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } - if (*m_cur_itr != ' ' && *m_cur_itr != '\t') { + if FK_YAML_UNLIKELY (*m_cur_itr != ' ' && *m_cur_itr != '\t') { emit_error("invalid tag handle is found."); } break; @@ -3027,7 +4440,7 @@ class lexical_analyzer { break; } char next = *(m_cur_itr + 1); - if (next != ' ' && next != '\t') { + if FK_YAML_UNLIKELY (next != ' ' && next != '\t') { emit_error("invalid tag handle is found."); } ends_loop = true; @@ -3036,14 +4449,14 @@ class lexical_analyzer { case '-': break; default: - if (!isalnum(*m_cur_itr)) { + if FK_YAML_UNLIKELY (!isalnum(*m_cur_itr)) { // See https://yaml.org/spec/1.2.2/#rule-c-named-tag-handle for more details. emit_error("named handle can contain only numbers(0-9), alphabets(A-Z,a-z) and hyphens(-)."); } break; } - if (++m_cur_itr == m_end_itr) { + if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) { emit_error("invalid TAG directive is found."); } } while (!ends_loop); @@ -3051,7 +4464,7 @@ class lexical_analyzer { } } - m_tag_handle.assign(m_token_begin_itr, m_cur_itr); + m_tag_handle = str_view {m_token_begin_itr, m_cur_itr}; skip_white_spaces(); @@ -3060,6 +4473,7 @@ class lexical_analyzer { // m_token_begin_itr = m_cur_itr; + const char* p_tag_prefix_begin = m_cur_itr; switch (*m_cur_itr) { // a tag prefix must not start with flow indicators to avoid ambiguity. // See https://yaml.org/spec/1.2.2/#rule-ns-global-tag-prefix for more details. @@ -3083,13 +4497,13 @@ class lexical_analyzer { } } while (!ends_loop && ++m_cur_itr != m_end_itr); - m_tag_prefix.assign(m_token_begin_itr, m_cur_itr); - - bool is_valid = uri_encoding::validate(m_tag_prefix.begin(), m_tag_prefix.end()); - if (!is_valid) { + bool is_valid = uri_encoding::validate(p_tag_prefix_begin, m_cur_itr); + if FK_YAML_UNLIKELY (!is_valid) { emit_error("invalid URI character is found in a tag prefix."); } + m_tag_prefix = str_view {p_tag_prefix_begin, m_cur_itr}; + return lexical_token_t::TAG_DIRECTIVE; } @@ -3097,7 +4511,6 @@ class lexical_analyzer { /// @note Only 1.1 and 1.2 are supported. If not, throws an exception. /// @return lexical_token_t The lexical token type for YAML version directives. lexical_token_t scan_yaml_version_directive() { - m_value_buffer.clear(); m_token_begin_itr = m_cur_itr; bool ends_loop = false; @@ -3114,20 +4527,20 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + m_yaml_version = str_view {m_token_begin_itr, m_cur_itr}; - if (m_value_buffer != "1.1" && m_value_buffer != "1.2") { + if FK_YAML_UNLIKELY (m_yaml_version.compare("1.1") != 0 && m_yaml_version.compare("1.2") != 0) { emit_error("Only 1.1 and 1.2 can be specified as the YAML version."); } return lexical_token_t::YAML_VER_DIRECTIVE; } - /// @brief Extracts an anchor name from the input and assigns the result to `m_value_buffer`. - void extract_anchor_name() { + /// @brief Extracts an anchor name from the input. + /// @param token The token into which the extraction result is written. + void extract_anchor_name(lexical_token& token) { FK_YAML_ASSERT(*m_cur_itr == '&' || *m_cur_itr == '*'); - m_value_buffer.clear(); m_token_begin_itr = ++m_cur_itr; bool ends_loop = false; @@ -3154,18 +4567,17 @@ class lexical_analyzer { } } - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + token.str = str_view {m_token_begin_itr, m_cur_itr}; } - /// @brief Extracts a tag name from the input and assigns the result to `m_value_buffer`. - void extract_tag_name() { - m_value_buffer.clear(); - + /// @brief Extracts a tag name from the input. + /// @param token The token into which the extraction result is written. + void extract_tag_name(lexical_token& token) { FK_YAML_ASSERT(*m_cur_itr == '!'); if (++m_cur_itr == m_end_itr) { // Just "!" is a non-specific tag. - m_value_buffer = "!"; + token.str = str_view {m_token_begin_itr, m_end_itr}; return; } @@ -3176,7 +4588,7 @@ class lexical_analyzer { case ' ': case '\n': // Just "!" is a non-specific tag. - m_value_buffer = "!"; + token.str = str_view {m_token_begin_itr, m_cur_itr}; return; case '!': // Secondary tag handles (!!suffix) @@ -3207,7 +4619,7 @@ class lexical_analyzer { ends_loop = true; break; case '!': - if (!allows_another_tag_prefix) { + if FK_YAML_UNLIKELY (!allows_another_tag_prefix) { emit_error("invalid tag prefix (!) is found."); } @@ -3220,22 +4632,22 @@ class lexical_analyzer { } } while (!ends_loop); - m_value_buffer.assign(m_token_begin_itr, m_cur_itr); + token.str = str_view {m_token_begin_itr, m_cur_itr}; if (is_verbatim) { - char last = m_value_buffer.back(); - if (last != '>') { + char last = token.str.back(); + if FK_YAML_UNLIKELY (last != '>') { emit_error("verbatim tag (!) must be ended with \'>\'."); } - auto tag_begin = m_value_buffer.begin() + 2; - auto tag_end = m_value_buffer.end() - 1; - if (tag_begin == tag_end) { + // only the `TAG` part of the `!` for URI validation. + str_view tag_body = token.str.substr(2, token.str.size() - 3); + if FK_YAML_UNLIKELY (tag_body.empty()) { emit_error("verbatim tag(!) must not be empty."); } - bool is_valid_uri = uri_encoding::validate(tag_begin, tag_end); - if (!is_valid_uri) { + bool is_valid_uri = uri_encoding::validate(tag_body.begin(), tag_body.end()); + if FK_YAML_UNLIKELY (!is_valid_uri) { emit_error("invalid URI character is found in a verbatim tag."); } @@ -3243,27 +4655,29 @@ class lexical_analyzer { } if (is_named_handle) { - char last = m_value_buffer.back(); - if (last == '!') { + char last = token.str.back(); + if FK_YAML_UNLIKELY (last == '!') { // Tag shorthand must be followed by a non-empty suffix. // See the "Tag Shorthands" section in https://yaml.org/spec/1.2.2/#691-node-tags. emit_error("named handle has no suffix."); } - std::size_t last_tag_prefix_pos = m_value_buffer.find_last_of('!'); - FK_YAML_ASSERT(last_tag_prefix_pos != std::string::npos); + // get the position of the beginning of a suffix. (!handle!suffix) + std::size_t last_tag_prefix_pos = token.str.find_last_of('!'); + FK_YAML_ASSERT(last_tag_prefix_pos != str_view::npos); - bool is_valid_uri = - uri_encoding::validate(m_value_buffer.begin() + last_tag_prefix_pos + 1, m_value_buffer.end()); - if (!is_valid_uri) { + str_view tag_uri = token.str.substr(last_tag_prefix_pos + 1); + bool is_valid_uri = uri_encoding::validate(tag_uri.begin(), tag_uri.end()); + if FK_YAML_UNLIKELY (!is_valid_uri) { emit_error("Invalid URI character is found in a named tag handle."); } } } - /// @brief Scan a string token, either plain, single-quoted or double-quoted. + /// @brief Scan a scalar token, either plain, single-quoted or double-quoted. + /// @param token The token into which the scan result is written. /// @return lexical_token_t The lexical token type for strings. - lexical_token_t scan_scalar() { + void scan_scalar(lexical_token& token) { m_value_buffer.clear(); bool needs_last_single_quote = false; @@ -3273,28 +4687,38 @@ class lexical_analyzer { needs_last_double_quote = (*m_cur_itr == '\"'); if (needs_last_double_quote || needs_last_single_quote) { m_token_begin_itr = ++m_cur_itr; + token.type = needs_last_double_quote ? lexical_token_t::DOUBLE_QUOTED_SCALAR + : lexical_token_t::SINGLE_QUOTED_SCALAR; + } + else { + token.type = lexical_token_t::PLAIN_SCALAR; } } - lexical_token_t type = extract_string_token(needs_last_single_quote, needs_last_double_quote); - FK_YAML_ASSERT(type == lexical_token_t::STRING_VALUE); + bool is_value_buff_used = extract_string_token(needs_last_single_quote, needs_last_double_quote); - if (needs_last_single_quote || needs_last_double_quote) { - // just returned the extracted string value if quoted. - return type; + if (is_value_buff_used) { + token.str = str_view {m_value_buffer.begin(), m_value_buffer.end()}; + } + else { + token.str = str_view {m_token_begin_itr, m_cur_itr}; + if (token.type != lexical_token_t::PLAIN_SCALAR) { + // If extract_string_token() didn't use m_value_buffer to store mutated scalar value, m_cur_itr is at + // the last quotation mark, which will cause infinite loops from the next get_next_token() call. + ++m_cur_itr; + } } - - return scalar_scanner::scan(m_value_buffer); } /// @brief Check if the given character is allowed in a single-quoted scalar token. /// @param c The character to be checked. + /// @param is_value_buffer_used true is assigned when mutated scalar contents is written into m_value_buffer. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_single(char c) { - bool ret = false; - + bool is_allowed_single(char c, bool& is_value_buffer_used) { switch (c) { case '\n': { + is_value_buffer_used = true; + // discard trailing white spaces which preceeds the line break in the current line. auto before_trailing_spaces_itr = m_cur_itr - 1; bool ends_loop = false; @@ -3332,41 +4756,47 @@ class lexical_analyzer { } m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\'') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; + return true; } case '\'': - // If single quotation marks are repeated twice in a single-quoted string token, - // they are considered as an escaped single quotation mark. if (m_cur_itr + 1 == m_end_itr) { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - m_token_begin_itr = m_cur_itr; - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + m_token_begin_itr = m_cur_itr; + } + return false; } if (*(m_cur_itr + 1) != '\'') { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + } + return false; } + // If single quotation marks are repeated twice in a single-quoted string token, + // they are considered as an escaped single quotation mark. + is_value_buffer_used = true; + m_value_buffer.append(m_token_begin_itr, ++m_cur_itr); m_token_begin_itr = m_cur_itr + 1; - ret = true; - break; - } + return true; - return ret; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } } /// @brief Check if the given character is allowed in a double-quoted scalar token. /// @param c The character to be checked. + /// @param is_value_buffer_used true is assigned when mutated scalar contents is written into m_value_buffer. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_double(char c) { - bool ret = false; - + bool is_allowed_double(char c, bool& is_value_buffer_used) { switch (c) { case '\n': { + is_value_buffer_used = true; + // discard trailing white spaces which preceeds the line break in the current line. auto before_trailing_spaces_itr = m_cur_itr - 1; bool ends_loop = false; @@ -3404,15 +4834,18 @@ class lexical_analyzer { } m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\"') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; + return true; } case '\"': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - break; + if (is_value_buffer_used) { + m_value_buffer.append(m_token_begin_itr, m_cur_itr++); + } + return false; case '\\': + is_value_buffer_used = true; + m_value_buffer.append(m_token_begin_itr, m_cur_itr); // Handle escaped characters. @@ -3421,13 +4854,12 @@ class lexical_analyzer { c = *(m_cur_itr + 1); if (c != '\n') { bool is_valid_escaping = yaml_escaper::unescape(m_cur_itr, m_end_itr, m_value_buffer); - if (!is_valid_escaping) { + if FK_YAML_UNLIKELY (!is_valid_escaping) { emit_error("Unsupported escape sequence is found in a string token."); } m_token_begin_itr = m_cur_itr + 1; - ret = true; - break; + return true; } // move until the next non-space character is found. @@ -3435,59 +4867,91 @@ class lexical_analyzer { skip_white_spaces(); m_token_begin_itr = (m_cur_itr == m_end_itr || *m_cur_itr == '\"') ? m_cur_itr-- : m_cur_itr; - ret = true; - break; - } + return true; - return ret; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } } - /// @brief Check if the given character is allowed in a plain scalar token. + /// @brief Check if the given character is allowed in a plain scalar token outside a flow context. /// @param c The character to be checked. /// @return true if the given character is allowed, false otherwise. - bool is_allowed_plain(char c) { - bool ret = false; + bool is_allowed_plain(char c, bool& /*unused*/) { + switch (c) { + case '\n': + return false; + + case ' ': { + // Allow a space in a plain scalar only if the space is surrounded by non-space characters. + // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. + + switch (*(m_cur_itr + 1)) { + case ':': { + // " :" is permitted in a plain style string token, but not when followed by a space. + char peeked = *(m_cur_itr + 2); + if (peeked == ' ') { + return false; + } + return true; + } + case ' ': + case '\n': + case '#': + case '\\': + return false; + } + return true; + } + + case ':': { + // A colon as a key separator must be followed by + // * a white space or + // * a newline code. + switch (*(m_cur_itr + 1)) { + case ' ': + case '\t': + case '\n': + return false; + } + return true; + } + + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE + } + } + + /// @brief Check if the given character is allowed in a plain scalar token inside a flow context. + /// @param c The character to be checked. + /// @return true if the given character is allowed, false otherwise. + bool is_allowed_plain_flow(char c, bool& /*unused*/) { switch (c) { case '\n': - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; + return false; case ' ': { // Allow a space in an unquoted string only if the space is surrounded by non-space characters. // See https://yaml.org/spec/1.2.2/#733-plain-style for more details. char next = *(m_cur_itr + 1); - bool is_appended = false; // These characters are permitted when not inside a flow collection, and not inside an implicit key. // TODO: Support detection of implicit key context for this check. - if (m_flow_context_depth > 0) { - switch (next) { - case '{': - case '}': - case '[': - case ']': - case ',': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - break; - } - - if (is_appended) { - break; - } + switch (next) { + case '{': + case '}': + case '[': + case ']': + case ',': + return false; } // " :" is permitted in a plain style string token, but not when followed by a space. if (next == ':') { char peeked = *(m_cur_itr + 2); if (peeked == ' ') { - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - } - - if (is_appended) { - break; + return false; } } @@ -3496,13 +4960,10 @@ class lexical_analyzer { case '\n': case '#': case '\\': - m_value_buffer.append(m_token_begin_itr, m_cur_itr++); - is_appended = true; - break; + return false; } - ret = !is_appended; - break; + return true; } case ':': { @@ -3515,13 +4976,9 @@ class lexical_analyzer { case ' ': case '\t': case '\n': - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; - default: - ret = true; - break; + return false; } - break; + return true; } case '{': @@ -3529,29 +4986,23 @@ class lexical_analyzer { case '[': case ']': case ',': - // just regard the flow indicators as a normal character if plain but not inside a flow context. - if (m_flow_context_depth == 0) { - ret = true; - break; - } + return false; - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - break; + default: // LCOV_EXCL_LINE + return true; // LCOV_EXCL_LINE } - - return ret; } /// @brief Extracts a string token, either plain, single-quoted or double-quoted, from the input buffer. - /// @return lexical_token_t The lexical token type for strings. - lexical_token_t extract_string_token(bool needs_last_single_quote, bool needs_last_double_quote) { + /// @return true if mutated scalar contents is stored in m_value_buffer, false otherwise. + bool extract_string_token(bool needs_last_single_quote, bool needs_last_double_quote) { // change behaviors depending on the type of a comming string scalar token. // * single quoted // * double quoted // * plain std::string check_filters {"\n"}; - bool (lexical_analyzer::*pfn_is_allowed)(char) = nullptr; + bool (lexical_analyzer::*pfn_is_allowed)(char, bool&) = nullptr; if (needs_last_single_quote) { check_filters.append("\'"); @@ -3561,30 +5012,36 @@ class lexical_analyzer { check_filters.append("\"\\"); pfn_is_allowed = &lexical_analyzer::is_allowed_double; } - else // plain scalars - { - check_filters.append(" :{}[],"); + else if (m_flow_context_depth == 0) { + // plain scalar outside flow contexts + check_filters.append(" :"); pfn_is_allowed = &lexical_analyzer::is_allowed_plain; } + else { + // plain scalar inside flow contexts + check_filters.append(" :{}[],"); + pfn_is_allowed = &lexical_analyzer::is_allowed_plain_flow; + } // scan the contents of a string scalar token. - for (; m_cur_itr != m_end_itr; m_cur_itr = (m_cur_itr == m_end_itr) ? m_cur_itr : ++m_cur_itr) { + bool is_value_buffer_used = false; + for (; m_cur_itr != m_end_itr; ++m_cur_itr) { char current = *m_cur_itr; uint32_t num_bytes = utf8::get_num_bytes(static_cast(current)); - if (num_bytes == 1) { + if FK_YAML_LIKELY (num_bytes == 1) { auto ret = check_filters.find(current); if (ret != std::string::npos) { - bool is_allowed = (this->*pfn_is_allowed)(current); + bool is_allowed = (this->*pfn_is_allowed)(current, is_value_buffer_used); if (!is_allowed) { - return lexical_token_t::STRING_VALUE; + return is_value_buffer_used; } continue; } // Handle unescaped control characters. - if (static_cast(current) <= 0x1F) { + if FK_YAML_UNLIKELY (static_cast(current) <= 0x1F) { handle_unescaped_control_char(current); continue; } @@ -3599,25 +5056,22 @@ class lexical_analyzer { // Handle the end of input buffer. - if (needs_last_double_quote) { + if FK_YAML_UNLIKELY (needs_last_double_quote) { emit_error("Invalid end of input buffer in a double-quoted string token."); } - if (needs_last_single_quote) { + if FK_YAML_UNLIKELY (needs_last_single_quote) { emit_error("Invalid end of input buffer in a single-quoted string token."); } - m_value_buffer.append(m_token_begin_itr, m_cur_itr); - return lexical_token_t::STRING_VALUE; + return is_value_buffer_used; } /// @brief Scan a block style string token either in the literal or folded style. /// @param style The style of the given token, either literal or folded. /// @param chomp The chomping indicator type of the given token, either strip, keep or clip. /// @param indent The indent size specified for the given token. - /// @return The lexical token type for strings. - lexical_token_t scan_block_style_string_token( - block_style_indicator_t style, chomping_indicator_t chomp, uint32_t indent) { + void scan_block_style_string_token(block_style_indicator_t style, chomping_indicator_t chomp, uint32_t indent) { m_value_buffer.clear(); // Handle leading all-space lines. @@ -3640,7 +5094,7 @@ class lexical_analyzer { if (chomp != chomping_indicator_t::KEEP) { m_value_buffer.clear(); } - return lexical_token_t::STRING_VALUE; + return; } m_pos_tracker.update_position(m_cur_itr); @@ -3650,7 +5104,7 @@ class lexical_analyzer { if (indent == 0) { indent = cur_indent; } - else if (cur_indent < indent) { + else if FK_YAML_UNLIKELY (cur_indent < indent) { emit_error("A block style scalar is less indented than the indicated level."); } @@ -3664,13 +5118,13 @@ class lexical_analyzer { } uint32_t diff = cur_indent - indent; - // m_value_buffer.append(diff, ' '); m_token_begin_itr -= diff; - chars_in_line += diff; + chars_in_line = diff; } for (; m_cur_itr != m_end_itr; ++m_cur_itr) { - if (*m_cur_itr == '\n') { + char current = *m_cur_itr; + if (current == '\n') { if (style == block_style_indicator_t::LITERAL) { if (chars_in_line == 0) { m_value_buffer.push_back('\n'); @@ -3700,12 +5154,12 @@ class lexical_analyzer { break; } - char current = *m_cur_itr; - if (current == ' ') { + char c = *m_cur_itr; + if (c == ' ') { continue; } - if (current == '\n') { + if (c == '\n') { is_next_empty = true; break; } @@ -3725,8 +5179,6 @@ class lexical_analyzer { chars_in_line = 0; continue; } - else { - } switch (char next = *(m_cur_itr + 1)) { case '\n': @@ -3755,7 +5207,7 @@ class lexical_analyzer { m_pos_tracker.update_position(m_cur_itr); cur_indent = m_pos_tracker.get_cur_pos_in_line(); if (cur_indent < indent) { - if (*m_cur_itr != ' ') { + if (current != ' ') { // Interpret less indented non-space characters as the start of the next token. break; } @@ -3763,7 +5215,7 @@ class lexical_analyzer { continue; } - if (*m_cur_itr == ' ' && style == block_style_indicator_t::FOLDED) { + if (current == ' ' && style == block_style_indicator_t::FOLDED) { // A line being more indented is not folded. m_value_buffer.push_back('\n'); is_extra_indented = true; @@ -3771,7 +5223,6 @@ class lexical_analyzer { m_token_begin_itr = m_cur_itr; } - // m_value_buffer.push_back(current); ++chars_in_line; } @@ -3797,21 +5248,21 @@ class lexical_analyzer { // No need to chomp the trailing newlines. break; } - while (m_value_buffer.size() > 1) { + uint32_t buf_size = static_cast(m_value_buffer.size()); + while (buf_size > 1) { // Strings with only newlines are handled above, so no check for the case. - char second_last = *(m_value_buffer.end() - 2); + char second_last = m_value_buffer[buf_size - 2]; if (second_last != '\n') { break; } m_value_buffer.pop_back(); + --buf_size; } break; } case chomping_indicator_t::KEEP: break; } - - return lexical_token_t::STRING_VALUE; } /// @brief Handle unescaped control characters. @@ -3890,30 +5341,53 @@ class lexical_analyzer { /// @param indent A variable to store the retrieved indent size. void get_block_style_metadata(chomping_indicator_t& chomp_type, uint32_t& indent) { chomp_type = chomping_indicator_t::CLIP; - switch (*++m_cur_itr) { - case '-': - chomp_type = chomping_indicator_t::STRIP; - ++m_cur_itr; - break; - case '+': - chomp_type = chomping_indicator_t::KEEP; - ++m_cur_itr; - break; - default: - break; - } + indent = 0; - if (*m_cur_itr == '0') { - emit_error("An indentation level for a block style scalar cannot be \'0\'"); - } + while (m_cur_itr != m_end_itr) { + switch (*m_cur_itr) { + case '-': + if FK_YAML_UNLIKELY (chomp_type != chomping_indicator_t::CLIP) { + emit_error("Too many block chomping indicators specified."); + } + chomp_type = chomping_indicator_t::STRIP; + break; + case '+': + if FK_YAML_UNLIKELY (chomp_type != chomping_indicator_t::CLIP) { + emit_error("Too many block chomping indicators specified."); + } + chomp_type = chomping_indicator_t::KEEP; + break; + case '0': + emit_error("An indentation level for a block scalar cannot be 0."); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if FK_YAML_UNLIKELY (indent > 0) { + emit_error("Invalid indentation level for a block scalar. It must be between 1 and 9."); + } + indent = static_cast(*m_cur_itr - '0'); + break; + case ' ': + case '\t': + break; + case '\n': + ++m_cur_itr; + return; + case '#': + skip_until_line_end(); + return; + default: + emit_error("Invalid character found in a block scalar header."); + } - indent = 0; - if (std::isdigit(*m_cur_itr)) { - indent = static_cast(*m_cur_itr++ - '0'); + ++m_cur_itr; } - - // skip characters including comments. - skip_until_line_end(); } /// @brief Skip white spaces (half-width spaces and tabs) from the current position. @@ -3960,21 +5434,23 @@ class lexical_analyzer { private: /// An input buffer adapter to be analyzed. - std::string m_input_buffer {}; + str_view m_input_buffer {}; /// The iterator to the current character in the input buffer. - std::string::const_iterator m_cur_itr {}; + const char* m_cur_itr {}; /// The iterator to the beginning of the current token. - std::string::const_iterator m_token_begin_itr {}; + const char* m_token_begin_itr {}; /// The iterator to the past-the-end element in the input buffer. - std::string::const_iterator m_end_itr {}; + const char* m_end_itr {}; /// The current position tracker of the input buffer. mutable position_tracker m_pos_tracker {}; /// A temporal buffer to store a string to be parsed to an actual token value. std::string m_value_buffer {}; + /// The last yaml version. + str_view m_yaml_version {}; /// The last tag handle. - std::string m_tag_handle {}; - /// The last tag prefix - std::string m_tag_prefix {}; + str_view m_tag_handle {}; + /// The last tag prefix. + str_view m_tag_prefix {}; /// The beginning position of the last lexical token. (zero origin) uint32_t m_last_token_begin_pos {0}; /// The beginning line of the last lexical token. (zero origin) @@ -3990,7 +5466,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -4014,7 +5490,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -4079,146 +5555,715 @@ class tag_resolver { private: static std::string normalize_tag_name( const std::string& tag, const std::shared_ptr& directives) { - if (tag.empty()) { + if FK_YAML_UNLIKELY (tag.empty()) { throw invalid_tag("tag must not be empty.", ""); } - if (tag[0] != '!') { + if FK_YAML_UNLIKELY (tag[0] != '!') { throw invalid_tag("tag must start with \'!\'", tag.c_str()); } - if (tag.size() == 1) { - // Non-specific tag ("!") will be interpreted as one of the following: - // * tag:yaml.org,2002:seq - // * tag:yaml.org,2002:map - // * tag:yaml.org,2002:str - // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags. - // The interpretation cannot take place here because the input lacks the corresponding value. - return tag; + if (tag.size() == 1) { + // Non-specific tag ("!") will be interpreted as one of the following: + // * tag:yaml.org,2002:seq + // * tag:yaml.org,2002:map + // * tag:yaml.org,2002:str + // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags. + // The interpretation cannot take place here because the input lacks the corresponding value. + return tag; + } + + std::string normalized {"!<"}; + switch (tag[1]) { + case '!': { + // handle a secondary tag handle (!!suffix -> !<[secondary][suffix]>) + bool is_null_or_empty = !directives || directives->secondary_handle_prefix.empty(); + if (is_null_or_empty) { + normalized += default_secondary_handle_prefix + tag.substr(2); + } + else { + normalized += directives->secondary_handle_prefix + tag.substr(2); + } + break; + } + case '<': + if (tag[2] == '!') { + bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty(); + if (is_null_or_empty) { + return normalized + default_primary_handle_prefix + tag.substr(3); + } + return normalized + directives->primary_handle_prefix + tag.substr(3); + } + + // verbatim tags must be delivered as-is to the application. + // See https://yaml.org/spec/1.2.2/#691-node-tags for more details. + return tag; + default: { + auto tag_end_pos = tag.find_first_of('!', 1); + + // handle a named handle (!tag!suffix -> !<[tag][suffix]>) + if (tag_end_pos != std::string::npos) { + // there must be a non-empty suffix. (already checked by the lexer.) + FK_YAML_ASSERT(tag_end_pos < tag.size() - 1); + + bool is_null_or_empty = !directives || directives->named_handle_map.empty(); + if FK_YAML_UNLIKELY (is_null_or_empty) { + throw invalid_tag("named handle has not been registered.", tag.c_str()); + } + + // find the extracted named handle in the map. + auto named_handle_itr = directives->named_handle_map.find(tag.substr(0, tag_end_pos + 1)); + auto end_itr = directives->named_handle_map.end(); + if FK_YAML_UNLIKELY (named_handle_itr == end_itr) { + throw invalid_tag("named handle has not been registered.", tag.c_str()); + } + + // The YAML spec prohibits expanding the percent-encoded characters (%xx -> a UTF-8 byte). + // So no conversion takes place. + // See https://yaml.org/spec/1.2.2/#56-miscellaneous-characters for more details. + + normalized += named_handle_itr->second; + normalized.append(tag.begin() + (tag_end_pos + 1), tag.end()); + break; + } + + // handle a primary tag handle (!suffix -> !<[primary][suffix]>) + bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty(); + if (is_null_or_empty) { + normalized += default_primary_handle_prefix + tag.substr(1); + } + else { + normalized += directives->primary_handle_prefix + tag.substr(1); + } + + break; + } + } + + normalized += ">"; + return normalized; + } + + static tag_t convert_to_tag_type(const std::string& normalized) { + if (normalized == "!") { + return tag_t::NON_SPECIFIC; + } + + if (normalized.size() < 24 /* size of !") { + return tag_t::SEQUENCE; + } + if (normalized == "!") { + return tag_t::MAPPING; + } + if (normalized == "!") { + return tag_t::NULL_VALUE; + } + if (normalized == "!") { + return tag_t::BOOLEAN; + } + if (normalized == "!") { + return tag_t::INTEGER; + } + if (normalized == "!") { + return tag_t::FLOATING_NUMBER; + } + if (normalized == "!") { + return tag_t::STRING; + } + + return tag_t::CUSTOM_TAG; + } +}; + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_INPUT_TAG_RESOLVER_HPP_ */ + +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ +#define FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ + +#include +#include +#include +#include + +// #include + +// #include + +// #include + + +#if defined(FK_YAML_HAS_CXX_17) && FK_YAML_HAS_INCLUDE() +#include +#endif + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/////////////////////////////////////////// +// Input Adapter API detection traits +/////////////////////////////////////////// + +/// @brief A type which represents get_buffer_view function. +/// @tparam T A target type. +template +using get_buffer_view_fn_t = decltype(std::declval().get_buffer_view()); + +/// @brief Type traits to check if InputAdapterType has get_buffer_view member function. +/// @tparam InputAdapterType An input adapter type to check if it has get_buffer_view function. +/// @tparam typename N/A +template +struct has_get_buffer_view : std::false_type {}; + +/// @brief A partial specialization of has_get_buffer_view if InputAdapterType has get_buffer_view member function. +/// @tparam InputAdapterType A type of a target input adapter. +template +struct has_get_buffer_view::value>> + : std::true_type {}; + +//////////////////////////////// +// is_input_adapter traits +//////////////////////////////// + +/// @brief Type traits to check if T is an input adapter type. +/// @tparam T A target type. +/// @tparam typename N/A +template +struct is_input_adapter : std::false_type {}; + +/// @brief A partial specialization of is_input_adapter if T is an input adapter type. +/// @tparam InputAdapterType +template +struct is_input_adapter::value>> : std::true_type { +}; + +///////////////////////////////////////////////// +// traits for contiguous iterator detection +///////////////////////////////////////////////// + +/// @brief Type traits to check if T is a container which has contiguous bytes. +/// @tparam T A target type. +template +struct is_contiguous_container : std::false_type {}; + +/// @brief A partial specialization of is_contiguous_container if T is a std::array. +/// @tparam T Element type. +/// @tparam N Maximum number of elements. +template +struct is_contiguous_container> : std::true_type {}; + +/// @brief A partial specialization of is_contiguous_container if T is a std::basic_string. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +/// @tparam Alloc Allocator type. +template +struct is_contiguous_container> : std::true_type {}; + +#ifdef FK_YAML_HAS_CXX_17 + +/// @brief A partial specialization of is_contiguous_container if T is a std::basic_string_view. +/// @tparam CharT Character type. +/// @tparam Traits Character traits type. +template +struct is_contiguous_container> : std::true_type {}; + +#endif // defined(FK_YAML_HAS_CXX_20) + +/// @brief A partial specialization of is_contiguous_container if T is a std::vector. +/// @tparam T Element type. +/// @tparam Alloc Allocator type. +template +struct is_contiguous_container> : std::true_type {}; + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP_ */ + +// #include + +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ +#define FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ + +#include +#include + +// #include + +// #include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +namespace { + +/// @brief Check if the given character is a digit. +/// @note This function is needed to avoid assertion failures in `std::isdigit()` especially when compiled with MSVC. +/// @param c A character to be checked. +/// @return true if the given character is a digit, false otherwise. +inline bool is_digit(char c) { + return ('0' <= c && c <= '9'); +} + +/// @brief Check if the given character is a hex-digit. +/// @note This function is needed to avoid assertion failures in `std::isxdigit()` especially when compiled with MSVC. +/// @param c A character to be checked. +/// @return true if the given character is a hex-digit, false otherwise. +inline bool is_xdigit(char c) { + return (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')); +} + +} // namespace + +/// @brief The class which detects a scalar value type by scanning contents. +class scalar_scanner { +public: + /// @brief Detects a scalar value type by scanning the contents ranged by the given iterators. + /// @param begin The iterator to the first element of the scalar. + /// @param end The iterator to the past-the-end element of the scalar. + /// @return A detected scalar value type. + static node_type scan(const char* begin, const char* end) { + if (begin == end) { + return node_type::STRING; + } + + uint32_t len = static_cast(std::distance(begin, end)); + if (len > 5) { + return scan_possible_number_token(begin, len); } - std::string normalized {"!<"}; - switch (tag[1]) { - case '!': { - // handle a secondary tag handle (!!suffix -> !<[secondary][suffix]>) - bool is_null_or_empty = !directives || directives->secondary_handle_prefix.empty(); - if (is_null_or_empty) { - normalized += default_secondary_handle_prefix + tag.substr(2); - } - else { - normalized += directives->secondary_handle_prefix + tag.substr(2); + const char* p_begin = &*begin; + + switch (len) { + case 1: + if (*p_begin == '~') { + return node_type::NULL_OBJECT; } break; - } - case '<': - if (tag[2] == '!') { - bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty(); - if (is_null_or_empty) { - return normalized + default_primary_handle_prefix + tag.substr(3); + case 4: + switch (*p_begin) { + case 'n': + // no possible case of begin a number otherwise. + return (std::strncmp(p_begin + 1, "ull", 3) == 0) ? node_type::NULL_OBJECT : node_type::STRING; + case 'N': + // no possible case of begin a number otherwise. + return ((std::strncmp(p_begin + 1, "ull", 3) == 0) || (std::strncmp(p_begin + 1, "ULL", 3) == 0)) + ? node_type::NULL_OBJECT + : node_type::STRING; + case 't': + // no possible case of being a number otherwise. + return (std::strncmp(p_begin + 1, "rue", 3) == 0) ? node_type::BOOLEAN : node_type::STRING; + case 'T': + // no possible case of being a number otherwise. + return ((std::strncmp(p_begin + 1, "rue", 3) == 0) || (std::strncmp(p_begin + 1, "RUE", 3) == 0)) + ? node_type::BOOLEAN + : node_type::STRING; + case '.': { + const char* p_from_second = p_begin + 1; + bool is_inf_or_nan_scalar = + (std::strncmp(p_from_second, "inf", 3) == 0) || (std::strncmp(p_from_second, "Inf", 3) == 0) || + (std::strncmp(p_from_second, "INF", 3) == 0) || (std::strncmp(p_from_second, "nan", 3) == 0) || + (std::strncmp(p_from_second, "NaN", 3) == 0) || (std::strncmp(p_from_second, "NAN", 3) == 0); + if (is_inf_or_nan_scalar) { + return node_type::FLOAT; } - return normalized + directives->primary_handle_prefix + tag.substr(3); + // maybe a number. + break; } - - // verbatim tags must be delivered as-is to the application. - // See https://yaml.org/spec/1.2.2/#691-node-tags for more details. - return tag; - default: { - auto tag_end_pos = tag.find_first_of('!', 1); - - // handle a named handle (!tag!suffix -> !<[tag][suffix]>) - if (tag_end_pos != std::string::npos) { - // there must be a non-empty suffix. (already checked by the lexer.) - FK_YAML_ASSERT(tag_end_pos < tag.size() - 1); - - bool is_null_or_empty = !directives || directives->named_handle_map.empty(); - if (is_null_or_empty) { - throw invalid_tag("named handle has not been registered.", tag.c_str()); - } - - // find the extracted named handle in the map. - auto named_handle_itr = directives->named_handle_map.find(tag.substr(0, tag_end_pos + 1)); - auto end_itr = directives->named_handle_map.end(); - if (named_handle_itr == end_itr) { - throw invalid_tag("named handle has not been registered.", tag.c_str()); + } + break; + case 5: + switch (*p_begin) { + case 'f': + // no possible case of being a number otherwise. + return (std::strncmp(p_begin + 1, "alse", 4) == 0) ? node_type::BOOLEAN : node_type::STRING; + case 'F': + // no possible case of being a number otherwise. + return ((std::strncmp(p_begin + 1, "alse", 4) == 0) || (std::strncmp(p_begin + 1, "ALSE", 4) == 0)) + ? node_type::BOOLEAN + : node_type::STRING; + case '+': + case '-': + if (*(p_begin + 1) == '.') { + const char* p_from_third = p_begin + 2; + bool is_min_inf_scalar = (std::strncmp(p_from_third, "inf", 3) == 0) || + (std::strncmp(p_from_third, "Inf", 3) == 0) || + (std::strncmp(p_from_third, "INF", 3) == 0); + if (is_min_inf_scalar) { + return node_type::FLOAT; + } } - - // The YAML spec prohibits expanding the percent-encoded characters (%xx -> a UTF-8 byte). - // So no conversion takes place. - // See https://yaml.org/spec/1.2.2/#56-miscellaneous-characters for more details. - - normalized += named_handle_itr->second; - normalized.append(tag.begin() + (tag_end_pos + 1), tag.end()); + // maybe a number. break; } + break; + } - // handle a primary tag handle (!suffix -> !<[primary][suffix]>) - bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty(); - if (is_null_or_empty) { - normalized += default_primary_handle_prefix + tag.substr(1); - } - else { - normalized += directives->primary_handle_prefix + tag.substr(1); - } + return scan_possible_number_token(begin, len); + } - break; +private: + /// @brief Detects a scalar value type from the contents (possibly an integer or a floating-point value). + /// @param itr The iterator to the first element of the scalar. + /// @param len The length of the scalar contents. + /// @return A detected scalar value type. + static node_type scan_possible_number_token(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); + + switch (*itr) { + case '-': + return (len > 1) ? scan_negative_number(++itr, --len) : node_type::STRING; + case '+': + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::STRING; + case '0': + return (len > 1) ? scan_after_zero_at_first(++itr, --len) : node_type::INTEGER; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::INTEGER; + default: + return node_type::STRING; } + } + + /// @brief Detects a scalar value type by scanning the contents right after the negative sign. + /// @param itr The iterator to the past-the-negative-sign element of the scalar. + /// @param len The length of the scalar contents left unscanned. + /// @return A detected scalar value type. + static node_type scan_negative_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); + + if (is_digit(*itr)) { + return (len > 1) ? scan_decimal_number(++itr, --len, false) : node_type::INTEGER; } - normalized += ">"; - return normalized; + return node_type::STRING; } - static tag_t convert_to_tag_type(const std::string& normalized) { - if (normalized == "!") { - return tag_t::NON_SPECIFIC; + /// @brief Detects a scalar value type by scanning the contents right after the beginning 0. + /// @param itr The iterator to the past-the-zero element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_after_zero_at_first(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); + + if (is_digit(*itr)) { + // a token consisting of the beginning '0' and some following numbers, e.g., `0123`, is not an integer + // according to https://yaml.org/spec/1.2.2/#10213-integer. + return node_type::STRING; } - if (normalized.size() < 24 /* size of ! 1) ? scan_octal_number(++itr, --len) : node_type::STRING; + case 'x': + return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::STRING; + default: + return node_type::STRING; } + } - if (normalized == "!") { - return tag_t::SEQUENCE; + /// @brief Detects a scalar value type by scanning the contents part starting with a decimal. + /// @param itr The iterator to the beginning decimal element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether a decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_decimal_number(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); + + if (is_digit(*itr)) { + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::INTEGER; } - if (normalized == "!") { - return tag_t::MAPPING; + + switch (*itr) { + case '.': { + if (has_decimal_point) { + // the token has more than one period, e.g., a semantic version `1.2.3`. + return node_type::STRING; + } + if (len == 1) { + // 0 is omitted after the decimal point + return node_type::FLOAT; + } + node_type ret = scan_after_decimal_point(++itr, --len, true); + return (ret == node_type::STRING) ? node_type::STRING : node_type::FLOAT; } - if (normalized == "!") { - return tag_t::NULL_VALUE; + case 'e': + case 'E': + return (len > 1) ? scan_after_exponent(++itr, --len, has_decimal_point) : node_type::STRING; + default: + return node_type::STRING; } - if (normalized == "!") { - return tag_t::BOOLEAN; + } + + /// @brief Detects a scalar value type by scanning the contents right after a decimal point. + /// @param itr The iterator to the past-the-decimal-point element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether the decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_after_decimal_point(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); + + if (is_digit(*itr)) { + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::FLOAT; } - if (normalized == "!") { - return tag_t::INTEGER; + + return node_type::STRING; + } + + /// @brief Detects a scalar value type by scanning the contents right after the exponent prefix ("e" or "E"). + /// @param itr The iterator to the past-the-exponent-prefix element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @param has_decimal_point Whether the decimal point has already been found in the previous part. + /// @return A detected scalar value type. + static node_type scan_after_exponent(const char* itr, uint32_t len, bool has_decimal_point) { + FK_YAML_ASSERT(len > 0); + + if (is_digit(*itr)) { + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::FLOAT; } - if (normalized == "!") { - return tag_t::FLOATING_NUMBER; + + switch (*itr) { + case '+': + case '-': + return (len > 1) ? scan_decimal_number(++itr, --len, has_decimal_point) : node_type::STRING; + default: + return node_type::STRING; } - if (normalized == "!") { - return tag_t::STRING; + } + + /// @brief Detects a scalar value type by scanning the contents assuming octal numbers. + /// @param itr The iterator to the octal-number element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_octal_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); + + switch (*itr) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + return (len > 1) ? scan_octal_number(++itr, --len) : node_type::INTEGER; + default: + return node_type::STRING; } + } - return tag_t::CUSTOM_TAG; + /// @brief Detects a scalar value type by scanning the contents assuming hexadecimal numbers. + /// @param itr The iterator to the hexadecimal-number element of the scalar. + /// @param len The length of the scalar left unscanned. + /// @return A detected scalar value type. + static node_type scan_hexadecimal_number(const char* itr, uint32_t len) { + FK_YAML_ASSERT(len > 0); + + if (is_xdigit(*itr)) { + return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::INTEGER; + } + return node_type::STRING; } }; FK_YAML_DETAIL_NAMESPACE_END -#endif /* FK_YAML_DETAIL_INPUT_TAG_RESOLVER_HPP_ */ +#endif /* FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP_ */ -// #include +// #include -// #include +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file -// #include +#ifndef FK_YAML_DETAIL_NODE_ATTRS_HPP_ +#define FK_YAML_DETAIL_NODE_ATTRS_HPP_ + +#include +#include + +// #include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief The type for node attribute bits. +using node_attr_t = uint32_t; + +/// @brief The namespace to define bit masks for node attribute bits. +namespace node_attr_mask { + +/// The bit mask for node value type bits. +const node_attr_t value = 0x0000FFFFu; +/// The bit mask for node style type bits. (bits are not yet defined.) +const node_attr_t style = 0x00FF0000u; +/// The bit mask for node property related bits. +const node_attr_t props = 0xFF000000u; +/// The bit mask for anchor/alias node type bits. +const node_attr_t anchoring = 0x03000000u; +/// The bit mask for anchor offset value bits. +const node_attr_t anchor_offset = 0xFC000000u; +/// The bit mask for all the bits for node attributes. +const node_attr_t all = std::numeric_limits::max(); + +} // namespace node_attr_mask + +/// @brief The namespace to define bits for node attributes. +namespace node_attr_bits { + +/// The sequence node bit. +const node_attr_t seq_bit = 1u << 0; +/// The mapping node bit. +const node_attr_t map_bit = 1u << 1; +/// The null scalar node bit. +const node_attr_t null_bit = 1u << 2; +/// The boolean scalar node bit. +const node_attr_t bool_bit = 1u << 3; +/// The integer scalar node bit. +const node_attr_t int_bit = 1u << 4; +/// The floating point scalar node bit. +const node_attr_t float_bit = 1u << 5; +/// The string scalar node bit. +const node_attr_t string_bit = 1u << 6; + +/// A utility bit set to filter scalar node bits. +const node_attr_t scalar_bits = null_bit | bool_bit | int_bit | float_bit | string_bit; + +/// The anchor node bit. +const node_attr_t anchor_bit = 0x01000000u; +/// The alias node bit. +const node_attr_t alias_bit = 0x02000000u; + +/// A utility bit set for initialization. +const node_attr_t default_bits = null_bit; + +/// @brief Converts a node_type value to a node_attr_t value. +/// @param t A type of node value. +/// @return The associated node value bit. +inline node_attr_t from_node_type(node_type t) noexcept { + switch (t) { + case node_type::SEQUENCE: + return seq_bit; + case node_type::MAPPING: + return map_bit; + case node_type::NULL_OBJECT: + return null_bit; + case node_type::BOOLEAN: + return bool_bit; + case node_type::INTEGER: + return int_bit; + case node_type::FLOAT: + return float_bit; + case node_type::STRING: + return string_bit; + default: // LCOV_EXCL_LINE + return node_attr_mask::all; // LCOV_EXCL_LINE + } +} + +/// @brief Converts a node_attr_t value to a node_type value. +/// @param bits node attribute bits +/// @return An associated node value type with the given node value bit. +inline node_type to_node_type(node_attr_t bits) noexcept { + switch (bits & node_attr_mask::value) { + case seq_bit: + return node_type::SEQUENCE; + case map_bit: + return node_type::MAPPING; + case null_bit: + return node_type::NULL_OBJECT; + case bool_bit: + return node_type::BOOLEAN; + case int_bit: + return node_type::INTEGER; + case float_bit: + return node_type::FLOAT; + case string_bit: + return node_type::STRING; + default: // LCOV_EXCL_LINE + return node_type::NULL_OBJECT; // LCOV_EXCL_LINE + } +} + +/// @brief Get an anchor offset used to reference an anchor node from the given attribute bits. +/// @param attrs node attribute bits +/// @return An anchor offset value. +inline uint32_t get_anchor_offset(node_attr_t attrs) noexcept { + return (attrs & node_attr_mask::anchor_offset) >> 26; +} + +/// @brief Set an anchor offset value to the appropriate bits. +/// @param offset An anchor offset value. +/// @param attrs node attribute bit set into which the offset value is written. +inline void set_anchor_offset(uint32_t offset, node_attr_t& attrs) noexcept { + attrs &= ~node_attr_mask::anchor_offset; + attrs |= (offset & 0x3Fu) << 26; +} + +} // namespace node_attr_bits + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_NODE_ATTRS_HPP_ */ // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -4236,21 +6281,11 @@ FK_YAML_DETAIL_NAMESPACE_END FK_YAML_DETAIL_NAMESPACE_BEGIN -enum class anchor_status_t { - NONE, - ANCHOR, - ALIAS, -}; - struct node_property { /// The tag name property. std::string tag {}; - /// The status regarding node anchoring/aliasing. - anchor_status_t anchor_status {anchor_status_t::NONE}; /// The anchor name property. std::string anchor {}; - /// The offset index value used to reference the anchor node implementation. - uint32_t anchor_offset {0}; }; FK_YAML_DETAIL_NAMESPACE_END @@ -4271,19 +6306,25 @@ class basic_deserializer { static_assert(is_basic_node::value, "basic_deserializer only accepts basic_node<...>"); /** A type for the target basic_node. */ - using node_type = BasicNodeType; + using basic_node_type = BasicNodeType; /** A type for the lexical analyzer. */ - using lexer_type = lexical_analyzer; + using lexer_type = lexical_analyzer; /** A type for the document metainfo. */ - using doc_metainfo_type = document_metainfo; + using doc_metainfo_type = document_metainfo; /** A type for the tag resolver. */ - using tag_resolver_type = tag_resolver; + using tag_resolver_type = tag_resolver; /** A type for sequence node value containers. */ - using sequence_type = typename node_type::sequence_type; + using sequence_type = typename basic_node_type::sequence_type; /** A type for mapping node value containers. */ - using mapping_type = typename node_type::mapping_type; + using mapping_type = typename basic_node_type::mapping_type; + /** A type for boolean node values. */ + using boolean_type = typename basic_node_type::boolean_type; + /** A type for integer node values. */ + using integer_type = typename basic_node_type::integer_type; + /** A type for floating point node values. */ + using float_number_type = typename basic_node_type::float_number_type; /** A type for string node values. */ - using string_type = typename node_type::string_type; + using string_type = typename basic_node_type::string_type; /// @brief Definition of state types of parse contexts. enum class context_state_t { @@ -4308,7 +6349,7 @@ class basic_deserializer { /// @param _indent The indentation width in the current line. (count from zero) /// @param _state The parse context type. /// @param _p_node The underlying node associated to this context. - parse_context(uint32_t _line, uint32_t _indent, context_state_t _state, node_type* _p_node) + parse_context(uint32_t _line, uint32_t _indent, context_state_t _state, basic_node_type* _p_node) : line(_line), indent(_indent), state(_state), @@ -4335,7 +6376,7 @@ class basic_deserializer { /// The parse context type. context_state_t state {context_state_t::BLOCK_MAPPING}; /// The pointer to the associated node to this context. - node_type* p_node {nullptr}; + basic_node_type* p_node {nullptr}; }; /// @brief Definitions of state types for expected flow token hints. @@ -4356,22 +6397,26 @@ class basic_deserializer { /// prefer the `deserialize_docs()` function. /// @tparam InputAdapterType The type of an input adapter object. /// @param input_adapter An input adapter object for the input source buffer. - /// @return node_type A root YAML node deserialized from the source string. + /// @return basic_node_type A root YAML node deserialized from the source string. template ::value, int> = 0> - node_type deserialize(InputAdapterType&& input_adapter) { + basic_node_type deserialize(InputAdapterType&& input_adapter) { + str_view input_view = input_adapter.get_buffer_view(); + lexer_type lexer(input_view); + lexical_token_t type {lexical_token_t::END_OF_BUFFER}; - lexer_type lexer(std::forward(input_adapter)); return deserialize_document(lexer, type); } /// @brief Deserialize multiple YAML documents into YAML nodes. /// @tparam InputAdapterType The type of an adapter object. /// @param input_adapter An input adapter object for the input source buffer. - /// @return std::vector Root YAML nodes for deserialized YAML documents. + /// @return std::vector Root YAML nodes for deserialized YAML documents. template ::value, int> = 0> - std::vector deserialize_docs(InputAdapterType&& input_adapter) { - lexer_type lexer(std::forward(input_adapter)); - std::vector nodes {}; + std::vector deserialize_docs(InputAdapterType&& input_adapter) { + str_view input_view = input_adapter.get_buffer_view(); + lexer_type lexer(input_view); + + std::vector nodes {}; lexical_token_t type {lexical_token_t::END_OF_BUFFER}; do { @@ -4385,24 +6430,24 @@ class basic_deserializer { /// @brief Deserialize a YAML document into a YAML node. /// @param lexer The lexical analyzer to be used. /// @param last_type The variable to store the last lexical token type. - /// @return node_type A root YAML node deserialized from the YAML document. - node_type deserialize_document(lexer_type& lexer, lexical_token_t& last_type) { - lexical_token_t type {lexical_token_t::END_OF_BUFFER}; + /// @return basic_node_type A root YAML node deserialized from the YAML document. + basic_node_type deserialize_document(lexer_type& lexer, lexical_token_t& last_type) { + lexical_token token {}; - node_type root; + basic_node_type root; mp_meta = root.mp_meta; // parse directives first. - deserialize_directives(lexer, type); + deserialize_directives(lexer, token); // parse node properties for root node if any uint32_t line = lexer.get_lines_processed(); uint32_t indent = lexer.get_last_token_begin_pos(); - bool found_props = deserialize_node_properties(lexer, type, line, indent); + bool found_props = deserialize_node_properties(lexer, token, line, indent); - switch (type) { + switch (token.type) { case lexical_token_t::SEQUENCE_BLOCK_PREFIX: { - root = node_type::sequence(); + root = basic_node_type::sequence(); apply_directive_set(root); if (found_props) { // If node properties are found before the block sequence entry prefix, the properties belong to the @@ -4412,29 +6457,29 @@ class basic_deserializer { parse_context context( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::BLOCK_SEQUENCE, &root); m_context_stack.emplace_back(std::move(context)); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; } case lexical_token_t::SEQUENCE_FLOW_BEGIN: ++m_flow_context_depth; - root = node_type::sequence(); + root = basic_node_type::sequence(); apply_directive_set(root); apply_node_properties(root); m_context_stack.emplace_back( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_SEQUENCE, &root); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; case lexical_token_t::MAPPING_FLOW_BEGIN: ++m_flow_context_depth; - root = node_type::mapping(); + root = basic_node_type::mapping(); apply_directive_set(root); apply_node_properties(root); m_context_stack.emplace_back( lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_MAPPING, &root); - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; default: { - root = node_type::mapping(); + root = basic_node_type::mapping(); apply_directive_set(root); if (found_props && line < lexer.get_lines_processed()) { // If node properties and a followed node are on the different line, the properties belong to the root @@ -4451,7 +6496,7 @@ class basic_deserializer { mp_current_node = &root; // parse YAML nodes recursively - deserialize_node(lexer, type, last_type); + deserialize_node(lexer, token, last_type); FK_YAML_ASSERT( last_type == lexical_token_t::END_OF_BUFFER || last_type == lexical_token_t::END_OF_DIRECTIVES || last_type == lexical_token_t::END_OF_DOCUMENT); @@ -4471,15 +6516,15 @@ class basic_deserializer { /// @brief Deserializes the YAML directives if specified. /// @param lexer The lexical analyzer to be used. /// @param last_type The variable to store the last lexical token type. - void deserialize_directives(lexer_type& lexer, lexical_token_t& last_type) { + void deserialize_directives(lexer_type& lexer, lexical_token& last_token) { bool lacks_end_of_directives_marker = false; for (;;) { - lexical_token_t type = lexer.get_next_token(); + lexical_token token = lexer.get_next_token(); - switch (type) { + switch (token.type) { case lexical_token_t::YAML_VER_DIRECTIVE: - if (mp_meta->is_version_specified) { + if FK_YAML_UNLIKELY (mp_meta->is_version_specified) { throw parse_error( "YAML version cannot be specified more than once.", lexer.get_lines_processed(), @@ -4491,36 +6536,41 @@ class basic_deserializer { lacks_end_of_directives_marker = true; break; case lexical_token_t::TAG_DIRECTIVE: { - const std::string& tag_handle = lexer.get_tag_handle(); - switch (tag_handle.size()) { - case 1: { + str_view tag_handle_view = lexer.get_tag_handle(); + switch (tag_handle_view.size()) { + case 1 /* ! */: { bool is_already_specified = !mp_meta->primary_handle_prefix.empty(); - if (is_already_specified) { + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "Primary handle cannot be specified more than once.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - mp_meta->primary_handle_prefix = lexer.get_tag_prefix(); + str_view tag_prefix = lexer.get_tag_prefix(); + mp_meta->primary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end()); lacks_end_of_directives_marker = true; break; } - case 2: { + case 2 /* !! */: { bool is_already_specified = !mp_meta->secondary_handle_prefix.empty(); - if (is_already_specified) { + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "Secondary handle cannot be specified more than once.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - mp_meta->secondary_handle_prefix = lexer.get_tag_prefix(); + str_view tag_prefix = lexer.get_tag_prefix(); + mp_meta->secondary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end()); lacks_end_of_directives_marker = true; break; } - default: { + default /* !! */: { + std::string tag_handle(tag_handle_view.begin(), tag_handle_view.end()); + str_view tag_prefix_view = lexer.get_tag_prefix(); + std::string tag_prefix(tag_prefix_view.begin(), tag_prefix_view.end()); bool is_already_specified = - !(mp_meta->named_handle_map.emplace(tag_handle, lexer.get_tag_prefix()).second); - if (is_already_specified) { + !(mp_meta->named_handle_map.emplace(std::move(tag_handle), std::move(tag_prefix)).second); + if FK_YAML_UNLIKELY (is_already_specified) { throw parse_error( "The same named handle cannot be specified more than once.", lexer.get_lines_processed(), @@ -4539,14 +6589,14 @@ class basic_deserializer { lacks_end_of_directives_marker = false; break; default: - if (lacks_end_of_directives_marker) { + if FK_YAML_UNLIKELY (lacks_end_of_directives_marker) { throw parse_error( "The end of directives marker (---) is missing after directives.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } // end the parsing of directives if the other tokens are found. - last_type = type; + last_token = token; return; } } @@ -4555,13 +6605,13 @@ class basic_deserializer { /// @brief Deserializes the YAML nodes recursively. /// @param lexer The lexical analyzer to be used. /// @param first_type The first lexical token type. - void deserialize_node(lexer_type& lexer, lexical_token_t first_type, lexical_token_t& last_type) { - lexical_token_t type = first_type; + void deserialize_node(lexer_type& lexer, const lexical_token& first_token, lexical_token_t& last_type) { + lexical_token token = first_token; uint32_t line = lexer.get_lines_processed(); uint32_t indent = lexer.get_last_token_begin_pos(); do { - switch (type) { + switch (token.type) { case lexical_token_t::EXPLICIT_KEY_PREFIX: { uint32_t pop_num = 0; if (indent == 0) { @@ -4585,7 +6635,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } - if (mp_current_node->is_null()) { + if FK_YAML_UNLIKELY (mp_current_node->is_null()) { // This path is needed in case the input contains nested explicit keys like the following YAML // snippet: // ```yaml @@ -4593,21 +6643,21 @@ class basic_deserializer { // : bar // : baz // ``` - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); } if (m_context_stack.back().state == context_state_t::BLOCK_SEQUENCE) { sequence_type& seq = mp_current_node->template get_value_ref(); - seq.emplace_back(node_type::mapping()); + seq.emplace_back(basic_node_type::mapping()); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, &(seq.back())); } - type = lexer.get_next_token(); - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + token = lexer.get_next_token(); + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event - m_context_stack.emplace_back( - line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new node_type(node_t::SEQUENCE)); + basic_node_type* p_node = new basic_node_type(node_type::SEQUENCE); + m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, p_node); mp_current_node = m_context_stack.back().p_node; apply_directive_set(*mp_current_node); parse_context context( @@ -4621,7 +6671,7 @@ class basic_deserializer { // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event m_context_stack.emplace_back( - line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new node_type()); + line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new basic_node_type()); mp_current_node = m_context_stack.back().p_node; apply_directive_set(*mp_current_node); indent = lexer.get_last_token_begin_pos(); @@ -4631,7 +6681,7 @@ class basic_deserializer { } case lexical_token_t::KEY_SEPARATOR: { bool is_empty_seq = mp_current_node->is_sequence() && mp_current_node->empty(); - if (is_empty_seq) { + if FK_YAML_UNLIKELY (is_empty_seq) { throw parse_error("sequence key should not be empty.", line, indent); } @@ -4639,11 +6689,11 @@ class basic_deserializer { uint32_t old_indent = indent; uint32_t old_line = line; - type = lexer.get_next_token(); + token = lexer.get_next_token(); line = lexer.get_lines_processed(); indent = lexer.get_last_token_begin_pos(); - bool found_props = deserialize_node_properties(lexer, type, line, indent); + bool found_props = deserialize_node_properties(lexer, token, line, indent); if (found_props && line == lexer.get_lines_processed()) { // defer applying node properties for the subsequent node on the same line. continue; @@ -4676,7 +6726,7 @@ class basic_deserializer { // ^ // this !!str tag overwrites the preceeding !!map tag. // ``` - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); @@ -4684,9 +6734,9 @@ class basic_deserializer { } } - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { // a key separator preceeding block sequence entries - *mp_current_node = node_type::sequence(); + *mp_current_node = basic_node_type::sequence(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); auto& cur_context = m_context_stack.back(); @@ -4694,11 +6744,11 @@ class basic_deserializer { cur_context.indent = indent; cur_context.state = context_state_t::BLOCK_SEQUENCE; - type = lexer.get_next_token(); + token = lexer.get_next_token(); line = lexer.get_lines_processed(); indent = lexer.get_last_token_begin_pos(); - bool has_props = deserialize_node_properties(lexer, type, line, indent); + bool has_props = deserialize_node_properties(lexer, token, line, indent); if (has_props) { uint32_t line_after_props = lexer.get_lines_processed(); if (line == line_after_props) { @@ -4719,7 +6769,7 @@ class basic_deserializer { line = line_after_props; indent = lexer.get_last_token_begin_pos(); mp_current_node->template get_value_ref().emplace_back( - node_type::mapping()); + basic_node_type::mapping()); mp_current_node = &mp_current_node->template get_value_ref().back(); m_context_stack.emplace_back( line_after_props, indent, context_state_t::BLOCK_MAPPING, mp_current_node); @@ -4740,15 +6790,16 @@ class basic_deserializer { m_context_stack.pop_back(); } - node_type key_node = std::move(*m_context_stack.back().p_node); + basic_node_type key_node = std::move(*m_context_stack.back().p_node); m_context_stack.pop_back(); - m_context_stack.back().p_node->template get_value_ref().emplace(key_node, node_type()); + m_context_stack.back().p_node->template get_value_ref().emplace( + key_node, basic_node_type()); mp_current_node = &(m_context_stack.back().p_node->operator[](std::move(key_node))); m_context_stack.emplace_back( line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_VALUE, mp_current_node); - if (type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { - *mp_current_node = node_type::sequence(); + if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) { + *mp_current_node = basic_node_type::sequence(); apply_directive_set(*mp_current_node); apply_node_properties(*mp_current_node); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node); @@ -4764,7 +6815,7 @@ class basic_deserializer { break; case lexical_token_t::ANCHOR_PREFIX: case lexical_token_t::TAG_PREFIX: - deserialize_node_properties(lexer, type, line, indent); + deserialize_node_properties(lexer, token, line, indent); // Skip updating the current indent to avoid stacking a wrong indentation. // Note that node properties for block sequences as a mapping value are processed when a // `lexical_token_t::KEY_SEPARATOR` token is processed. @@ -4778,7 +6829,7 @@ class basic_deserializer { case lexical_token_t::SEQUENCE_BLOCK_PREFIX: { bool is_further_nested = m_context_stack.back().indent < indent; if (is_further_nested) { - mp_current_node->template get_value_ref().emplace_back(node_type::sequence()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::sequence()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node); apply_directive_set(*mp_current_node); @@ -4818,7 +6869,7 @@ class basic_deserializer { } }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -4832,7 +6883,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("Flow sequence begininng is found without separated with a comma.", line, indent); } @@ -4841,7 +6892,7 @@ class basic_deserializer { switch (m_context_stack.back().state) { case context_state_t::BLOCK_SEQUENCE: case context_state_t::FLOW_SEQUENCE: - mp_current_node->template get_value_ref().emplace_back(node_type::sequence()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::sequence()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::FLOW_SEQUENCE, mp_current_node); break; @@ -4849,11 +6900,11 @@ class basic_deserializer { case context_state_t::FLOW_MAPPING: // heap-allocated node will be freed in handling the corresponding SEQUENCE_FLOW_END event. m_context_stack.emplace_back( - line, indent, context_state_t::FLOW_SEQUENCE_KEY, new node_type(node_t::SEQUENCE)); + line, indent, context_state_t::FLOW_SEQUENCE_KEY, new basic_node_type(node_type::SEQUENCE)); mp_current_node = m_context_stack.back().p_node; break; default: { - *mp_current_node = node_type::sequence(); + *mp_current_node = basic_node_type::sequence(); parse_context& last_context = m_context_stack.back(); last_context.line = line; last_context.indent = indent; @@ -4868,7 +6919,7 @@ class basic_deserializer { m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::SEQUENCE_FLOW_END: { - if (m_flow_context_depth == 0) { + if FK_YAML_UNLIKELY (m_flow_context_depth == 0) { throw parse_error("Flow sequence ending is found outside the flow context.", line, indent); } --m_flow_context_depth; @@ -4888,7 +6939,7 @@ class basic_deserializer { }); bool is_valid = itr != m_context_stack.rend(); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw parse_error("No corresponding flow sequence beginning is found.", line, indent); } @@ -4903,7 +6954,7 @@ class basic_deserializer { // handle cases where the flow sequence is a mapping key node. if (!m_context_stack.empty() && state == context_state_t::FLOW_SEQUENCE_KEY) { - node_type key_node = std::move(*mp_current_node); + basic_node_type key_node = std::move(*mp_current_node); delete mp_current_node; mp_current_node = m_context_stack.back().p_node; m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; @@ -4912,9 +6963,9 @@ class basic_deserializer { break; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { - node_type key_node = node_type::mapping(); + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { + basic_node_type key_node = basic_node_type::mapping(); apply_directive_set(key_node); mp_current_node->swap(key_node); @@ -4960,7 +7011,7 @@ class basic_deserializer { } }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -4974,7 +7025,7 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("Flow mapping begininng is found without separated with a comma.", line, indent); } @@ -4983,7 +7034,7 @@ class basic_deserializer { switch (m_context_stack.back().state) { case context_state_t::BLOCK_SEQUENCE: case context_state_t::FLOW_SEQUENCE: - mp_current_node->template get_value_ref().emplace_back(node_type::mapping()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::mapping()); mp_current_node = &(mp_current_node->template get_value_ref().back()); m_context_stack.emplace_back(line, indent, context_state_t::FLOW_MAPPING, mp_current_node); break; @@ -4991,11 +7042,11 @@ class basic_deserializer { case context_state_t::FLOW_MAPPING: // heap-allocated node will be freed in handling the corresponding MAPPING_FLOW_END event. m_context_stack.emplace_back( - line, indent, context_state_t::FLOW_MAPPING_KEY, new node_type(node_t::MAPPING)); + line, indent, context_state_t::FLOW_MAPPING_KEY, new basic_node_type(node_type::MAPPING)); mp_current_node = m_context_stack.back().p_node; break; default: { - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); parse_context& last_context = m_context_stack.back(); last_context.line = line; last_context.indent = indent; @@ -5013,7 +7064,7 @@ class basic_deserializer { m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::MAPPING_FLOW_END: { - if (m_flow_context_depth == 0) { + if FK_YAML_UNLIKELY (m_flow_context_depth == 0) { throw parse_error("Flow mapping ending is found outside the flow context.", line, indent); } --m_flow_context_depth; @@ -5033,7 +7084,7 @@ class basic_deserializer { }); bool is_valid = itr != m_context_stack.rend(); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw parse_error("No corresponding flow mapping beginning is found.", line, indent); } @@ -5048,7 +7099,7 @@ class basic_deserializer { // handle cases where the flow mapping is a mapping key node. if (!m_context_stack.empty() && state == context_state_t::FLOW_MAPPING_KEY) { - node_type key_node = std::move(*mp_current_node); + basic_node_type key_node = std::move(*mp_current_node); delete mp_current_node; mp_current_node = m_context_stack.back().p_node; m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; @@ -5057,9 +7108,9 @@ class basic_deserializer { break; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { - node_type key_node = node_type::mapping(); + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { + basic_node_type key_node = basic_node_type::mapping(); apply_directive_set(key_node); mp_current_node->swap(key_node); @@ -5083,18 +7134,17 @@ class basic_deserializer { } case lexical_token_t::VALUE_SEPARATOR: FK_YAML_ASSERT(m_flow_context_depth > 0); - if (m_flow_token_state != flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { + if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) { throw parse_error("invalid value separator is found.", line, indent); } m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX; break; case lexical_token_t::ALIAS_PREFIX: - case lexical_token_t::NULL_VALUE: - case lexical_token_t::BOOLEAN_VALUE: - case lexical_token_t::INTEGER_VALUE: - case lexical_token_t::FLOAT_NUMBER_VALUE: - case lexical_token_t::STRING_VALUE: { - bool do_continue = deserialize_scalar(lexer, indent, line, type); + case lexical_token_t::PLAIN_SCALAR: + case lexical_token_t::SINGLE_QUOTED_SCALAR: + case lexical_token_t::DOUBLE_QUOTED_SCALAR: + case lexical_token_t::BLOCK_SCALAR: { + bool do_continue = deserialize_scalar(lexer, indent, line, token); if (do_continue) { continue; } @@ -5104,16 +7154,16 @@ class basic_deserializer { case lexical_token_t::END_OF_BUFFER: // This handles an empty input. case lexical_token_t::END_OF_DIRECTIVES: case lexical_token_t::END_OF_DOCUMENT: - last_type = type; + last_type = token.type; return; } - type = lexer.get_next_token(); + token = lexer.get_next_token(); indent = lexer.get_last_token_begin_pos(); line = lexer.get_lines_processed(); - } while (type != lexical_token_t::END_OF_BUFFER); + } while (token.type != lexical_token_t::END_OF_BUFFER); - last_type = type; + last_type = token.type; } /// @brief Deserializes YAML node properties (anchor and/or tag names) if they exist @@ -5122,26 +7172,26 @@ class basic_deserializer { /// @param line The variable to store the line of either the first property or the last non-property token. /// @param indent The variable to store the indent of either the first property or the last non-property token. /// @return true if any property is found, false otherwise. - bool deserialize_node_properties(lexer_type& lexer, lexical_token_t& last_type, uint32_t& line, uint32_t& indent) { + bool deserialize_node_properties(lexer_type& lexer, lexical_token& last_token, uint32_t& line, uint32_t& indent) { m_needs_anchor_impl = m_needs_tag_impl = false; - lexical_token_t type = last_type; + lexical_token token = last_token; bool ends_loop {false}; do { if (line < lexer.get_lines_processed()) { break; } - switch (type) { + switch (token.type) { case lexical_token_t::ANCHOR_PREFIX: - if (m_needs_anchor_impl) { + if FK_YAML_UNLIKELY (m_needs_anchor_impl) { throw parse_error( "anchor name cannot be specified more than once to the same node.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - m_anchor_name = lexer.get_string(); + m_anchor_name.assign(token.str.begin(), token.str.end()); m_needs_anchor_impl = true; if (!m_needs_tag_impl) { @@ -5149,17 +7199,17 @@ class basic_deserializer { indent = lexer.get_last_token_begin_pos(); } - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; case lexical_token_t::TAG_PREFIX: { - if (m_needs_tag_impl) { + if FK_YAML_UNLIKELY (m_needs_tag_impl) { throw parse_error( "tag name cannot be specified more than once to the same node.", lexer.get_lines_processed(), lexer.get_last_token_begin_pos()); } - m_tag_name = lexer.get_string(); + m_tag_name.assign(token.str.begin(), token.str.end()); m_needs_tag_impl = true; if (!m_needs_anchor_impl) { @@ -5167,7 +7217,7 @@ class basic_deserializer { indent = lexer.get_last_token_begin_pos(); } - type = lexer.get_next_token(); + token = lexer.get_next_token(); break; } default: @@ -5176,7 +7226,7 @@ class basic_deserializer { } } while (!ends_loop); - last_type = type; + last_token = token; bool prop_specified = m_needs_anchor_impl || m_needs_tag_impl; if (!prop_specified) { line = lexer.get_lines_processed(); @@ -5190,7 +7240,7 @@ class basic_deserializer { /// @param key a key string to be added to the current YAML node. /// @param line The line where the key is found. /// @param indent The indentation width in the current line where the key is found. - void add_new_key(node_type&& key, const uint32_t line, const uint32_t indent) { + void add_new_key(basic_node_type&& key, const uint32_t line, const uint32_t indent) { if (m_flow_context_depth == 0) { uint32_t pop_num = 0; if (indent == 0) { @@ -5203,7 +7253,7 @@ class basic_deserializer { return (indent == c.indent) && (c.state == context_state_t::BLOCK_MAPPING); }); bool is_indent_valid = (target_itr != m_context_stack.rend()); - if (!is_indent_valid) { + if FK_YAML_UNLIKELY (!is_indent_valid) { throw parse_error("Detected invalid indentaion.", line, indent); } @@ -5217,18 +7267,18 @@ class basic_deserializer { mp_current_node = m_context_stack.back().p_node; } } - else if (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { + else if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { throw parse_error("Flow mapping entry is found without separated with a comma.", line, indent); } if (mp_current_node->is_sequence()) { - mp_current_node->template get_value_ref().emplace_back(node_type::mapping()); + mp_current_node->template get_value_ref().emplace_back(basic_node_type::mapping()); mp_current_node = &(mp_current_node->operator[](mp_current_node->size() - 1)); m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); } - auto itr = mp_current_node->template get_value_ref().emplace(std::move(key), node_type()); - if (!itr.second) { + auto itr = mp_current_node->template get_value_ref().emplace(std::move(key), basic_node_type()); + if FK_YAML_UNLIKELY (!itr.second) { throw parse_error("Detected duplication in mapping keys.", line, indent); } @@ -5239,11 +7289,11 @@ class basic_deserializer { } /// @brief Assign node value to the current node. - /// @param node_value A rvalue node_type object to be assigned to the current node. - void assign_node_value(node_type&& node_value, const uint32_t line, const uint32_t indent) { + /// @param node_value A rvalue basic_node_type object to be assigned to the current node. + void assign_node_value(basic_node_type&& node_value, const uint32_t line, const uint32_t indent) { if (mp_current_node->is_sequence()) { if (m_flow_context_depth > 0) { - if (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { + if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) { throw parse_error("flow sequence entry is found without separated with a comma.", line, indent); } m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX; @@ -5271,14 +7321,20 @@ class basic_deserializer { /// @param indent The last indent size. /// @param line The last line. /// @return The created YAML scalar node. - node_type create_scalar_node(lexer_type& lexer, lexical_token_t type, uint32_t indent, uint32_t line) { + basic_node_type create_scalar_node(const lexical_token& token, uint32_t indent, uint32_t line) { + lexical_token_t type = token.type; FK_YAML_ASSERT( - type == lexical_token_t::NULL_VALUE || type == lexical_token_t::BOOLEAN_VALUE || - type == lexical_token_t::INTEGER_VALUE || type == lexical_token_t::FLOAT_NUMBER_VALUE || - type == lexical_token_t::STRING_VALUE || type == lexical_token_t::ALIAS_PREFIX); + type == lexical_token_t::PLAIN_SCALAR || type == lexical_token_t::SINGLE_QUOTED_SCALAR || + type == lexical_token_t::DOUBLE_QUOTED_SCALAR || type == lexical_token_t::BLOCK_SCALAR || + type == lexical_token_t::ALIAS_PREFIX); + + node_type value_type {node_type::STRING}; + if (type == lexical_token_t::PLAIN_SCALAR) { + value_type = scalar_scanner::scan(token.str.begin(), token.str.end()); + } if (m_needs_tag_impl) { - if (type == lexical_token_t::ALIAS_PREFIX) { + if FK_YAML_UNLIKELY (type == lexical_token_t::ALIAS_PREFIX) { throw parse_error("Tag cannot be specified to alias nodes", line, indent); } @@ -5288,24 +7344,24 @@ class basic_deserializer { switch (tag_type) { case tag_t::NULL_VALUE: - type = lexical_token_t::NULL_VALUE; + value_type = node_type::NULL_OBJECT; break; case tag_t::BOOLEAN: - type = lexical_token_t::BOOLEAN_VALUE; + value_type = node_type::BOOLEAN; break; case tag_t::INTEGER: - type = lexical_token_t::INTEGER_VALUE; + value_type = node_type::INTEGER; break; case tag_t::FLOATING_NUMBER: - type = lexical_token_t::FLOAT_NUMBER_VALUE; + value_type = node_type::FLOAT; break; case tag_t::STRING: - type = lexical_token_t::STRING_VALUE; + value_type = node_type::STRING; break; case tag_t::NON_SPECIFIC: // scalars with the non-specific tag is resolved to a string tag. // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags. - type = lexical_token_t::STRING_VALUE; + value_type = node_type::STRING; break; case tag_t::CUSTOM_TAG: default: @@ -5313,34 +7369,66 @@ class basic_deserializer { } } - node_type node {}; - switch (type) { - case lexical_token_t::NULL_VALUE: - node = node_type(lexer.get_null()); - break; - case lexical_token_t::BOOLEAN_VALUE: - node = node_type(lexer.get_boolean()); - break; - case lexical_token_t::INTEGER_VALUE: - node = node_type(lexer.get_integer()); + basic_node_type node {}; + + if (type == lexical_token_t::ALIAS_PREFIX) { + const std::string token_str = std::string(token.str.begin(), token.str.end()); + + uint32_t anchor_counts = static_cast(mp_meta->anchor_table.count(token_str)); + if FK_YAML_UNLIKELY (anchor_counts == 0) { + throw parse_error("The given anchor name must appear prior to the alias node.", line, indent); + } + + node.m_attrs |= detail::node_attr_bits::alias_bit; + node.m_prop.anchor = std::move(token_str); + detail::node_attr_bits::set_anchor_offset(anchor_counts - 1, node.m_attrs); + + apply_directive_set(node); + apply_node_properties(node); + + return node; + } + + switch (value_type) { + case node_type::NULL_OBJECT: { + std::nullptr_t null = nullptr; + bool converted = detail::aton(token.str.begin(), token.str.end(), null); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a null.", line, indent); + } + // The above `node` variable is already null, so no instance creation is needed. break; - case lexical_token_t::FLOAT_NUMBER_VALUE: - node = node_type(lexer.get_float_number()); + } + case node_type::BOOLEAN: { + boolean_type boolean = static_cast(false); + bool converted = detail::atob(token.str.begin(), token.str.end(), boolean); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a boolean.", line, indent); + } + node = basic_node_type(boolean); break; - case lexical_token_t::STRING_VALUE: - node = node_type(lexer.get_string()); + } + case node_type::INTEGER: { + integer_type integer = 0; + bool converted = detail::atoi(token.str.begin(), token.str.end(), integer); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to an integer.", line, indent); + } + node = basic_node_type(integer); break; - case lexical_token_t::ALIAS_PREFIX: { - const string_type& alias_name = lexer.get_string(); - uint32_t anchor_counts = static_cast(mp_meta->anchor_table.count(alias_name)); - if (anchor_counts == 0) { - throw parse_error("The given anchor name must appear prior to the alias node.", line, indent); + } + case node_type::FLOAT: { + float_number_type float_val = 0; + bool converted = detail::atof(token.str.begin(), token.str.end(), float_val); + if FK_YAML_UNLIKELY (!converted) { + throw parse_error("Failed to convert a scalar to a floating point value", line, indent); } - node.m_prop.anchor_status = detail::anchor_status_t::ALIAS; - node.m_prop.anchor = alias_name; - node.m_prop.anchor_offset = anchor_counts - 1; + node = basic_node_type(float_val); break; } + case node_type::STRING: + node = basic_node_type(std::string(token.str.begin(), token.str.end())); + break; default: // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } @@ -5357,16 +7445,16 @@ class basic_deserializer { /// @param indent The current indentation width. Can be updated in this function. /// @param line The number of processed lines. Can be updated in this function. /// @return true if next token has already been got, false otherwise. - bool deserialize_scalar(lexer_type& lexer, uint32_t& indent, uint32_t& line, lexical_token_t& type) { - node_type node = create_scalar_node(lexer, type, indent, line); + bool deserialize_scalar(lexer_type& lexer, uint32_t& indent, uint32_t& line, lexical_token& token) { + basic_node_type node = create_scalar_node(token, indent, line); if (mp_current_node->is_mapping()) { add_new_key(std::move(node), line, indent); return false; } - type = lexer.get_next_token(); - if (type == lexical_token_t::KEY_SEPARATOR) { + token = lexer.get_next_token(); + if (token.type == lexical_token_t::KEY_SEPARATOR) { if (line != lexer.get_lines_processed()) { // This path is for explicit mapping key separator like: // @@ -5393,7 +7481,7 @@ class basic_deserializer { m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node); break; default: - if (cur_context.line == line) { + if FK_YAML_UNLIKELY (cur_context.line == line) { throw parse_error("Multiple mapping keys are specified on the same line.", line, indent); } cur_context.line = line; @@ -5402,7 +7490,7 @@ class basic_deserializer { break; } - *mp_current_node = node_type::mapping(); + *mp_current_node = basic_node_type::mapping(); apply_directive_set(*mp_current_node); } add_new_key(std::move(node), line, indent); @@ -5416,14 +7504,14 @@ class basic_deserializer { } /// @brief Set YAML directive properties to the given node. - /// @param node A node_type object to be set YAML directive properties. - void apply_directive_set(node_type& node) noexcept { + /// @param node A basic_node_type object to be set YAML directive properties. + void apply_directive_set(basic_node_type& node) noexcept { node.mp_meta = mp_meta; } /// @brief Set YAML node properties (anchor and/or tag names) to the given node. /// @param node A node type object to be set YAML node properties. - void apply_node_properties(node_type& node) { + void apply_node_properties(basic_node_type& node) { if (m_needs_anchor_impl) { node.add_anchor_name(m_anchor_name); m_needs_anchor_impl = false; @@ -5439,13 +7527,13 @@ class basic_deserializer { /// @brief Update the target YAML version with an input string. /// @param version_str A YAML version string. - yaml_version_t convert_yaml_version(const string_type& version_str) noexcept { - return (version_str == "1.1") ? yaml_version_t::VER_1_1 : yaml_version_t::VER_1_2; + yaml_version_type convert_yaml_version(str_view version_str) noexcept { + return (version_str.compare("1.1") == 0) ? yaml_version_type::VERSION_1_1 : yaml_version_type::VERSION_1_2; } private: /// The currently focused YAML node. - node_type* mp_current_node {nullptr}; + basic_node_type* mp_current_node {nullptr}; /// The stack of parse contexts. std::deque m_context_stack {}; /// The current depth of flow contexts. @@ -5459,9 +7547,9 @@ class basic_deserializer { /// A flag to determine the need for a value separator or a flow suffix to follow. flow_token_state_t m_flow_token_state {flow_token_state_t::NEEDS_VALUE_OR_SUFFIX}; /// The last YAML anchor name. - string_type m_anchor_name {}; + std::string m_anchor_name {}; /// The last tag name. - string_type m_tag_name {}; + std::string m_tag_name {}; }; FK_YAML_DETAIL_NAMESPACE_END @@ -5471,7 +7559,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -5493,10 +7581,10 @@ FK_YAML_DETAIL_NAMESPACE_END // #include -// #include +// #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -5504,8 +7592,8 @@ FK_YAML_DETAIL_NAMESPACE_END /// /// @file -#ifndef FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ -#define FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ +#ifndef FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ +#define FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ #include #include @@ -5515,7 +7603,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -5543,7 +7631,11 @@ enum class utf_encode_t { FK_YAML_DETAIL_NAMESPACE_END -#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_T_HPP_ */ +#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_T_HPP_ */ + +// #include + +// #include // #include @@ -5558,65 +7650,79 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN inline utf_encode_t detect_encoding_type(const std::array& bytes, bool& has_bom) noexcept { has_bom = false; + uint8_t byte0 = bytes[0]; + uint8_t byte1 = bytes[1]; + uint8_t byte2 = bytes[2]; + uint8_t byte3 = bytes[3]; + // Check if a BOM exists. - if (bytes[0] == uint8_t(0xEFu) && bytes[1] == uint8_t(0xBBu) && bytes[2] == uint8_t(0xBFu)) { + if (byte0 == uint8_t(0xEFu) && byte1 == uint8_t(0xBBu) && byte2 == uint8_t(0xBFu)) { has_bom = true; return utf_encode_t::UTF_8; } - if (bytes[0] == 0 && bytes[1] == 0 && bytes[2] == uint8_t(0xFEu) && bytes[3] == uint8_t(0xFFu)) { + if (byte0 == 0 && byte1 == 0 && byte2 == uint8_t(0xFEu) && byte3 == uint8_t(0xFFu)) { has_bom = true; return utf_encode_t::UTF_32BE; } - if (bytes[0] == uint8_t(0xFFu) && bytes[1] == uint8_t(0xFEu) && bytes[2] == 0 && bytes[3] == 0) { + if (byte0 == uint8_t(0xFFu) && byte1 == uint8_t(0xFEu) && byte2 == 0 && byte3 == 0) { has_bom = true; return utf_encode_t::UTF_32LE; } - if (bytes[0] == uint8_t(0xFEu) && bytes[1] == uint8_t(0xFFu)) { + if (byte0 == uint8_t(0xFEu) && byte1 == uint8_t(0xFFu)) { has_bom = true; return utf_encode_t::UTF_16BE; } - if (bytes[0] == uint8_t(0xFFu) && bytes[1] == uint8_t(0xFEu)) { + if (byte0 == uint8_t(0xFFu) && byte1 == uint8_t(0xFEu)) { has_bom = true; return utf_encode_t::UTF_16LE; } // Test the first character assuming it's an ASCII character. - if (bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0 && 0 < bytes[3] && bytes[3] < uint8_t(0x80u)) { + if (byte0 == 0 && byte1 == 0 && byte2 == 0 && 0 < byte3 && byte3 < uint8_t(0x80u)) { return utf_encode_t::UTF_32BE; } - if (0 < bytes[0] && bytes[0] < uint8_t(0x80u) && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0) { + if (0 < byte0 && byte0 < uint8_t(0x80u) && byte1 == 0 && byte2 == 0 && byte3 == 0) { return utf_encode_t::UTF_32LE; } - if (bytes[0] == 0 && 0 < bytes[1] && bytes[1] < uint8_t(0x80u)) { + if (byte0 == 0 && 0 < byte1 && byte1 < uint8_t(0x80u)) { return utf_encode_t::UTF_16BE; } - if (0 < bytes[0] && bytes[0] < uint8_t(0x80u) && bytes[1] == 0) { + if (0 < byte0 && byte0 < uint8_t(0x80u) && byte1 == 0) { return utf_encode_t::UTF_16LE; } return utf_encode_t::UTF_8; } -/// @brief Detects the encoding type of the input, and consumes a BOM if it exists. +/// @brief A class which detects UTF encoding type and the existence of a BOM at the beginning. /// @tparam ItrType Type of iterators for the input. -/// @tparam ElemSize The size of one input element. -/// @param begin The beginning of input iterators. -/// @param end The end of input iterators. -/// @return A detected encoding type. -template ())))> -inline utf_encode_t detect_encoding_and_skip_bom(ItrType& begin, const ItrType& end) { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - switch (ElemSize) { - case sizeof(char): { // this case covers char8_t as well when compiled with C++20 or better. +template +struct utf_encode_detector {}; + +/// @brief The partial specialization of utf_encode_detector for char iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) noexcept { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_8; + } + + std::array bytes {}; + bytes.fill(0xFFu); for (int i = 0; i < 4 && begin + i != end; i++) { bytes[i] = uint8_t(begin[i]); } @@ -5643,19 +7749,72 @@ inline utf_encode_t detect_encoding_and_skip_bom(ItrType& begin, const ItrType& return encode_type; } - case sizeof(char16_t): { - if (begin == end) { +}; + +#if FK_YAML_HAS_CHAR8_T + +/// @brief The partial specialization of utf_encode_detector for char8_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { + return utf_encode_t::UTF_8; + } + + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4 && begin + i != end; i++) { + bytes[i] = uint8_t(begin[i]); + } + + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_8) { + throw exception("char8_t characters must be encoded in the UTF-8 format."); + } + + if (has_bom) { + // skip reading the BOM. + std::advance(begin, 3); + } + + return encode_type; + } +}; + +#endif // FK_YAML_HAS_CHAR8_T + +/// @brief The partial specialization of utf_encode_detector for char16_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { return utf_encode_t::UTF_16BE; } + + std::array bytes {}; + bytes.fill(0xFFu); for (int i = 0; i < 2 && begin + i != end; i++) { - bytes[i * 2] = uint8_t((begin[i] & 0xFF00u) >> 8); - bytes[i * 2 + 1] = uint8_t(begin[i] & 0xFFu); + char16_t elem = begin[i]; + bytes[i * 2] = uint8_t((elem & 0xFF00u) >> 8); + bytes[i * 2 + 1] = uint8_t(elem & 0xFFu); } bool has_bom = false; utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - if (encode_type != utf_encode_t::UTF_16BE && encode_type != utf_encode_t::UTF_16LE) { + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_16BE && encode_type != utf_encode_t::UTF_16LE) { throw exception("char16_t characters must be encoded in the UTF-16 format."); } @@ -5666,20 +7825,32 @@ inline utf_encode_t detect_encoding_and_skip_bom(ItrType& begin, const ItrType& return encode_type; } - case sizeof(char32_t): { - if (begin == end) { +}; + +/// @brief The partial specialization of utf_encode_detector for char32_t iterators. +/// @tparam ItrType An iterator type. +template +struct utf_encode_detector::value>> { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param begin The iterator to the first element of an input. + /// @param end The iterator to the past-the end element of an input. + /// @return A detected encoding type. + static utf_encode_t detect(ItrType& begin, const ItrType& end) { + if FK_YAML_UNLIKELY (begin == end) { return utf_encode_t::UTF_32BE; } - bytes[0] = uint8_t((*begin & 0xFF000000u) >> 24); - bytes[1] = uint8_t((*begin & 0x00FF0000u) >> 16); - bytes[2] = uint8_t((*begin & 0x0000FF00u) >> 8); - bytes[3] = uint8_t(*begin & 0x000000FFu); + std::array bytes {}; + char32_t elem = *begin; + bytes[0] = uint8_t((elem & 0xFF000000u) >> 24); + bytes[1] = uint8_t((elem & 0x00FF0000u) >> 16); + bytes[2] = uint8_t((elem & 0x0000FF00u) >> 8); + bytes[3] = uint8_t(elem & 0x000000FFu); bool has_bom = false; utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - if (encode_type != utf_encode_t::UTF_32BE && encode_type != utf_encode_t::UTF_32LE) { + if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_32BE && encode_type != utf_encode_t::UTF_32LE) { throw exception("char32_t characters must be encoded in the UTF-32 format."); } @@ -5690,96 +7861,111 @@ inline utf_encode_t detect_encoding_and_skip_bom(ItrType& begin, const ItrType& return encode_type; } - default: - throw exception("Unknown char size."); - } -} +}; -inline utf_encode_t detect_encoding_and_skip_bom(std::FILE* file) noexcept { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - for (int i = 0; i < 4; i++) { - char byte = 0; - std::size_t size = std::fread(&byte, sizeof(char), 1, file); - if (size != sizeof(char)) { - break; +/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file. +struct file_utf_encode_detector { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param p_file The input file handle. + /// @return A detected encoding type. + static utf_encode_t detect(std::FILE* p_file) noexcept { + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4; i++) { + char byte = 0; + std::size_t size = std::fread(&byte, sizeof(char), 1, p_file); + if (size != sizeof(char)) { + break; + } + bytes[i] = uint8_t(byte & 0xFF); } - bytes[i] = uint8_t(byte & 0xFF); - } - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - // move back to the beginning if a BOM doesn't exist. - long offset = 0; - if (has_bom) { - switch (encode_type) { - case utf_encode_t::UTF_8: - offset = 3; - break; - case utf_encode_t::UTF_16BE: - case utf_encode_t::UTF_16LE: - offset = 2; - break; - case utf_encode_t::UTF_32BE: - case utf_encode_t::UTF_32LE: - offset = 4; - break; + // move back to the beginning if a BOM doesn't exist. + long offset = 0; + if (has_bom) { + switch (encode_type) { + case utf_encode_t::UTF_8: + offset = 3; + break; + case utf_encode_t::UTF_16BE: + case utf_encode_t::UTF_16LE: + offset = 2; + break; + case utf_encode_t::UTF_32BE: + case utf_encode_t::UTF_32LE: + offset = 4; + break; + } } - } - fseek(file, offset, SEEK_SET); + std::fseek(p_file, offset, SEEK_SET); - return encode_type; -} + return encode_type; + } +}; -inline utf_encode_t detect_encoding_and_skip_bom(std::istream& is) noexcept { - std::array bytes = {{0xFFu, 0xFFu, 0xFFu, 0xFFu}}; - for (int i = 0; i < 4; i++) { - char ch = 0; - is.read(&ch, 1); - std::streamsize size = is.gcount(); - if (size != 1) { - // without this, seekg() fails in the switch-case statement below. - is.clear(); - break; +/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file. +struct stream_utf_encode_detector { + /// @brief Detects the encoding type of the input, and consumes a BOM if it exists. + /// @param p_file The input file handle. + /// @return A detected encoding type. + static utf_encode_t detect(std::istream& is) noexcept { + std::array bytes {}; + bytes.fill(0xFFu); + for (int i = 0; i < 4; i++) { + char ch = 0; + is.read(&ch, 1); + std::streamsize size = is.gcount(); + if (size != 1) { + // without this, seekg() will fail. + is.clear(); + break; + } + bytes[i] = uint8_t(ch & 0xFF); } - bytes[i] = uint8_t(ch & 0xFF); - } - bool has_bom = false; - utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); + bool has_bom = false; + utf_encode_t encode_type = detect_encoding_type(bytes, has_bom); - // move back to the beginning if a BOM doesn't exist. - std::streamoff offset = 0; - if (has_bom) { - switch (encode_type) { - case utf_encode_t::UTF_8: - offset = 3; - break; - case utf_encode_t::UTF_16BE: - case utf_encode_t::UTF_16LE: - offset = 2; - break; - case utf_encode_t::UTF_32BE: - case utf_encode_t::UTF_32LE: - offset = 4; - break; + // move back to the beginning if a BOM doesn't exist. + std::streamoff offset = 0; + if (has_bom) { + switch (encode_type) { + case utf_encode_t::UTF_8: + offset = 3; + break; + case utf_encode_t::UTF_16BE: + case utf_encode_t::UTF_16LE: + offset = 2; + break; + case utf_encode_t::UTF_32BE: + case utf_encode_t::UTF_32LE: + offset = 4; + break; + } } - } - is.seekg(offset, std::ios_base::beg); + is.seekg(offset, std::ios_base::beg); - return encode_type; -} + return encode_type; + } +}; FK_YAML_DETAIL_NAMESPACE_END -#endif /* FK_YAML_DETAIL_ENCODINGS_ENCODE_DETECTOR_HPP_ */ +#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP_ */ // #include // #include +// #include + // #include +// #include + // #include @@ -5795,9 +7981,7 @@ class iterator_input_adapter; /// @brief An input adapter for iterators of type char. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -5806,10 +7990,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { } // allow only move construct/assignment like other input adapters. @@ -5819,34 +8005,32 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { - buffer.clear(); - buffer.reserve(std::distance(m_current, m_end)); + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { + m_buffer.clear(); switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of fill_buffer() for UTF-8 encoded inputs. - /// @param buffer A buffer to be filled with the input. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); - IterType current = m_current; + IterType current = m_begin; while (current != m_end) { uint8_t first = uint8_t(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -5855,7 +8039,7 @@ class iterator_input_adapter< case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -5863,7 +8047,7 @@ class iterator_input_adapter< case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -5872,7 +8056,7 @@ class iterator_input_adapter< std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -5883,18 +8067,36 @@ class iterator_input_adapter< } } + IterType cr_or_end_itr = std::find(m_begin, m_end, '\r'); + if (cr_or_end_itr == m_end && m_is_contiguous) { + // The input iterators (begin, end) can be used as-is during parsing. + return str_view {m_begin, m_end}; + } + + m_buffer.reserve(std::distance(m_begin, m_end)); + + current = m_begin; do { - IterType cr_or_end_itr = std::find(m_current, m_end, '\r'); - buffer.append(m_current, cr_or_end_itr); - m_current = (cr_or_end_itr == m_end) ? cr_or_end_itr : std::next(cr_or_end_itr); - } while (m_current != m_end); + m_buffer.append(current, cr_or_end_itr); + if (cr_or_end_itr == m_end) { + break; + } + current = std::next(cr_or_end_itr); + cr_or_end_itr = std::find(current, m_end, '\r'); + } while (current != m_end); + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @param buffer A buffer to be filled with the input. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end) / 2); + int shift_bits[2] {0, 0}; if (m_encode_type == utf_encode_t::UTF_16BE) { shift_bits[0] = 8; @@ -5909,11 +8111,14 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - while (m_current != m_end || encoded_buf_size != 0) { - while (m_current != m_end && encoded_buf_size < 2) { - char16_t utf16 = static_cast(uint8_t(*m_current++) << shift_bits[0]); - utf16 |= static_cast(uint8_t(*m_current++) << shift_bits[1]); - if (utf16 != char16_t(0x000Du)) { + IterType current = m_begin; + while (current != m_end || encoded_buf_size != 0) { + while (current != m_end && encoded_buf_size < 2) { + char16_t utf16 = static_cast(uint8_t(*current++) << shift_bits[0]); + utf16 |= static_cast(uint8_t(*current++) << shift_bits[1]); + + // skip appending CRs. + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -5921,20 +8126,26 @@ class iterator_input_adapter< uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end) / 4); + int shift_bits[4] {0, 0, 0, 0}; if (m_encode_type == utf_encode_t::UTF_32BE) { shift_bits[0] = 24; @@ -5951,36 +8162,41 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - while (m_current != m_end) { - char32_t utf32 = static_cast(*m_current++ << shift_bits[0]); - utf32 |= static_cast(*m_current++ << shift_bits[1]); - utf32 |= static_cast(*m_current++ << shift_bits[2]); - utf32 |= static_cast(*m_current++ << shift_bits[3]); + IterType current = m_begin; + while (current != m_end) { + char32_t utf32 = static_cast(*current++ << shift_bits[0]); + utf32 |= static_cast(*current++ << shift_bits[1]); + utf32 |= static_cast(*current++ << shift_bits[2]); + utf32 |= static_cast(*current++ << shift_bits[3]); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; -#ifdef FK_YAML_HAS_CHAR8_T +#if FK_YAML_HAS_CHAR8_T /// @brief An input adapter for iterators of type char8_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char8_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -5989,10 +8205,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { // char8_t characters must be encoded in the UTF-8 format. // See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0482r6.html. FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); @@ -6005,10 +8223,10 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { - IterType current = m_current; + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { + IterType current = m_begin; while (current != m_end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -6017,7 +8235,7 @@ class iterator_input_adapter< case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6025,7 +8243,7 @@ class iterator_input_adapter< case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6034,7 +8252,7 @@ class iterator_input_adapter< std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6045,32 +8263,38 @@ class iterator_input_adapter< } } - buffer.reserve(std::distance(m_current, m_end)); - while (m_current != m_end) { - char c = char(*m_current++); - if (c != '\r') { - buffer.push_back(c); + m_buffer.reserve(std::distance(m_begin, m_end)); + current = m_begin; + + while (current != m_end) { + char c = char(*current++); + if FK_YAML_LIKELY (c != '\r') { + m_buffer.push_back(c); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; -#endif // defined(FK_YAML_HAS_CHAR8_T) +#endif // FK_YAML_HAS_CHAR8_T /// @brief An input adapter for iterators of type char16_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char16_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -6079,10 +8303,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); } @@ -6093,9 +8319,9 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { int shift_bits = (m_encode_type == utf_encode_t::UTF_16BE) ? 0 : 8; std::array encoded_buffer {{0, 0}}; @@ -6103,16 +8329,19 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - buffer.reserve(std::distance(m_current, m_end) * 2); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end)); - while (m_current != m_end || encoded_buf_size != 0) { - while (m_current != m_end && encoded_buf_size < 2) { - char16_t utf16 = *m_current++; + IterType current = m_begin; + while (current != m_end || encoded_buf_size != 0) { + while (current != m_end && encoded_buf_size < 2) { + char16_t utf16 = *current++; utf16 = char16_t( static_cast((utf16 & 0x00FFu) << shift_bits) | static_cast((utf16 & 0xFF00u) >> shift_bits)); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -6120,31 +8349,35 @@ class iterator_input_adapter< uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; encoded_buffer[1] = 0; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_16BE}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; /// @brief An input adapter for iterators of type char32_t. /// @tparam IterType An iterator type. template -class iterator_input_adapter< - IterType, - enable_if_t::value_type>, char32_t>::value>> { +class iterator_input_adapter::value>> { public: /// @brief Construct a new iterator_input_adapter object. iterator_input_adapter() = default; @@ -6153,10 +8386,12 @@ class iterator_input_adapter< /// @param begin The beginning of iteraters. /// @param end The end of iterators. /// @param encode_type The encoding type for this input adapter. - iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type) noexcept - : m_current(begin), + /// @param is_contiguous Whether iterators are contiguous or not. + iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept + : m_begin(begin), m_end(end), - m_encode_type(encode_type) { + m_encode_type(encode_type), + m_is_contiguous(is_contiguous) { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); } @@ -6167,9 +8402,9 @@ class iterator_input_adapter< iterator_input_adapter& operator=(iterator_input_adapter&&) = default; ~iterator_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { int shift_bits[4] {0, 0, 0, 0}; if (m_encode_type == utf_encode_t::UTF_32LE) { shift_bits[0] = 24; @@ -6181,30 +8416,39 @@ class iterator_input_adapter< std::array utf8_buffer {{0, 0, 0, 0}}; uint32_t utf8_buf_size {0}; - buffer.reserve(std::distance(m_current, m_end) * 4); + // Assume the input characters are all ASCII characters. + // That's the most probably the case. + m_buffer.reserve(std::distance(m_begin, m_end)); - while (m_current != m_end) { - char32_t tmp = *m_current++; + IterType current = m_begin; + while (current != m_end) { + char32_t tmp = *current++; char32_t utf32 = char32_t( static_cast((tmp & 0xFF000000u) >> shift_bits[0]) | static_cast((tmp & 0x00FF0000u) >> shift_bits[1]) | static_cast((tmp & 0x0000FF00u) << shift_bits[2]) | static_cast((tmp & 0x000000FFu) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_UNLIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: - /// The iterator at the current position. - IterType m_current {}; + /// The iterator at the beginning of input. + IterType m_begin {}; /// The iterator at the end of input. IterType m_end {}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_32BE}; + /// The normalized owned buffer. + std::string m_buffer {}; + /// Whether or not ItrType is a contiguous iterator. + bool m_is_contiguous {false}; }; /// @brief An input adapter for C-style file handles. @@ -6231,28 +8475,27 @@ class file_input_adapter { file_input_adapter& operator=(file_input_adapter&&) = default; ~file_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of get_character() for UTF-8 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); char tmp_buf[256] {}; @@ -6271,13 +8514,13 @@ class file_input_adapter { ++p_cr_or_end; } - buffer.append(p_current, p_cr_or_end); + m_buffer.append(p_current, p_cr_or_end); p_current = (p_cr_or_end == p_end) ? p_end : p_cr_or_end + 1; } while (p_current != p_end); } - auto current = buffer.begin(); - auto end = buffer.end(); + auto current = m_buffer.begin(); + auto end = m_buffer.end(); while (current != end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -6286,7 +8529,7 @@ class file_input_adapter { case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6294,7 +8537,7 @@ class file_input_adapter { case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6303,7 +8546,7 @@ class file_input_adapter { std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6313,11 +8556,13 @@ class file_input_adapter { break; } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); int shift_bits[2] {0, 0}; @@ -6339,7 +8584,7 @@ class file_input_adapter { char16_t utf16 = char16_t( static_cast(uint8_t(chars[0]) << shift_bits[0]) | static_cast(uint8_t(chars[1]) << shift_bits[1])); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } } @@ -6347,18 +8592,20 @@ class file_input_adapter { uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); int shift_bits[4] {0, 0, 0, 0}; @@ -6380,7 +8627,7 @@ class file_input_adapter { while (std::feof(m_file) == 0) { std::size_t size = std::fread(&chars[0], sizeof(char), 4, m_file); if (size != 4) { - return; + break; } char32_t utf32 = char32_t( @@ -6389,11 +8636,13 @@ class file_input_adapter { static_cast(uint8_t(chars[2]) << shift_bits[2]) | static_cast(uint8_t(chars[3]) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: @@ -6401,6 +8650,8 @@ class file_input_adapter { std::FILE* m_file {nullptr}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; }; /// @brief An input adapter for streams @@ -6411,6 +8662,7 @@ class stream_input_adapter { /// @brief Construct a new stream_input_adapter object. /// @param is A reference to the target input stream. + /// @param encode_type The encoding type for this input adapter. explicit stream_input_adapter(std::istream& is, utf_encode_t encode_type) noexcept : m_istream(&is), m_encode_type(encode_type) { @@ -6423,28 +8675,27 @@ class stream_input_adapter { stream_input_adapter& operator=(stream_input_adapter&&) = default; ~stream_input_adapter() = default; - /// @brief Get a character at the current position and move forward. - /// @return std::char_traits::int_type A character or EOF. - void fill_buffer(std::string& buffer) { + /// @brief Get view into the input buffer contents. + /// @return View into the input buffer contents. + str_view get_buffer_view() { switch (m_encode_type) { case utf_encode_t::UTF_8: - fill_buffer_utf8(buffer); - break; + return get_buffer_view_utf8(); case utf_encode_t::UTF_16BE: case utf_encode_t::UTF_16LE: - fill_buffer_utf16(buffer); - break; + return get_buffer_view_utf16(); case utf_encode_t::UTF_32BE: case utf_encode_t::UTF_32LE: - fill_buffer_utf32(buffer); - break; + return get_buffer_view_utf32(); + default: // LCOV_EXCL_LINE + return {}; // LCOV_EXCL_LINE } } private: - /// @brief The concrete implementation of get_character() for UTF-8 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf8(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf8() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8); char tmp_buf[256] {}; @@ -6464,13 +8715,13 @@ class stream_input_adapter { ++p_cr_or_end; } - buffer.append(p_current, p_cr_or_end); + m_buffer.append(p_current, p_cr_or_end); p_current = (p_cr_or_end == p_end) ? p_end : p_cr_or_end + 1; } while (p_current != p_end); } while (!m_istream->eof()); - auto current = buffer.begin(); - auto end = buffer.end(); + auto current = m_buffer.begin(); + auto end = m_buffer.end(); while (current != end) { uint8_t first = static_cast(*current++); uint32_t num_bytes = utf8::get_num_bytes(first); @@ -6479,7 +8730,7 @@ class stream_input_adapter { case 2: { std::initializer_list bytes {first, uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6487,7 +8738,7 @@ class stream_input_adapter { case 3: { std::initializer_list bytes {first, uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6496,7 +8747,7 @@ class stream_input_adapter { std::initializer_list bytes { first, uint8_t(*current++), uint8_t(*current++), uint8_t(*current++)}; bool is_valid = utf8::validate(bytes); - if (!is_valid) { + if FK_YAML_UNLIKELY (!is_valid) { throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", bytes); } break; @@ -6506,11 +8757,13 @@ class stream_input_adapter { break; } } + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-16 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf16(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf16() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE); int shift_bits[2] {0, 0}; @@ -6539,7 +8792,7 @@ class stream_input_adapter { static_cast(uint8_t(chars[0]) << shift_bits[0]) | static_cast(uint8_t(chars[1]) << shift_bits[1])); - if (utf16 != char16_t(0x000Du)) { + if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) { encoded_buffer[encoded_buf_size++] = utf16; } }; @@ -6547,18 +8800,20 @@ class stream_input_adapter { uint32_t consumed_size = 0; utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size); - if (consumed_size == 1) { + if FK_YAML_LIKELY (consumed_size == 1) { encoded_buffer[0] = encoded_buffer[1]; } encoded_buf_size -= consumed_size; - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } while (!m_istream->eof()); + + return str_view {m_buffer.begin(), m_buffer.end()}; } - /// @brief The concrete implementation of get_character() for UTF-32 encoded inputs. - /// @return A UTF-8 encoded byte at the current position, or EOF. - void fill_buffer_utf32(std::string& buffer) { + /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs. + /// @return View into the UTF-8 encoded input buffer contents. + str_view get_buffer_view_utf32() { FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE); int shift_bits[4] {0, 0, 0, 0}; @@ -6581,7 +8836,7 @@ class stream_input_adapter { m_istream->read(&chars[0], 4); std::streamsize size = m_istream->gcount(); if (size != 4) { - return; + break; } char32_t utf32 = char32_t( @@ -6590,11 +8845,13 @@ class stream_input_adapter { static_cast(uint8_t(chars[2]) << shift_bits[2]) | static_cast(uint8_t(chars[3]) << shift_bits[3])); - if (utf32 != char32_t(0x0000000Du)) { + if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) { utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size); - buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); + m_buffer.append(reinterpret_cast(utf8_buffer.data()), utf8_buf_size); } } while (!m_istream->eof()); + + return str_view {m_buffer.begin(), m_buffer.end()}; } private: @@ -6602,21 +8859,47 @@ class stream_input_adapter { std::istream* m_istream {nullptr}; /// The encoding type for this input adapter. utf_encode_t m_encode_type {utf_encode_t::UTF_8}; + /// The normalized owned buffer. + std::string m_buffer {}; }; ///////////////////////////////// // input_adapter providers // ///////////////////////////////// +namespace { + +template +inline iterator_input_adapter create_iterator_input_adapter( + ItrType begin, ItrType end, bool is_contiguous) noexcept { + utf_encode_t encode_type = utf_encode_detector::detect(begin, end); + return iterator_input_adapter(begin, end, encode_type, is_contiguous); +} + +} // anonymous namespace + /// @brief A factory method for iterator_input_adapter objects with ieterator values. /// @tparam ItrType An iterator type. /// @param begin The beginning of iterators. /// @param end The end of iterators. /// @return iterator_input_adapter An iterator_input_adapter object for the target iterator type. -template ())))> +template inline iterator_input_adapter input_adapter(ItrType begin, ItrType end) { - utf_encode_t encode_type = detect_encoding_and_skip_bom(begin, end); - return iterator_input_adapter(begin, end, encode_type); + constexpr bool is_random_access_itr = + std::is_same::iterator_category, std::random_access_iterator_tag>::value; + + // Check if `begin` & `end` are contiguous iterators. + // Getting distance between begin and (end - 1) avoids dereferencing an invalid sentinel. + bool is_contiguous = false; + if (is_random_access_itr) { + ptrdiff_t size = static_cast(std::distance(begin, end - 1)); + + using CharPtr = remove_cvref_t::pointer>; + CharPtr p_begin = &*begin; + CharPtr p_second_last = &*(end - 1); + is_contiguous = (p_second_last - p_begin == size); + } + return create_iterator_input_adapter(begin, end, is_contiguous); } /// @brief A factory method for iterator_input_adapter objects with C-style arrays. @@ -6624,8 +8907,8 @@ inline iterator_input_adapter input_adapter(ItrType begin, ItrType end) /// @tparam N A size of an array. /// @return decltype(input_adapter(array, array + N)) An iterator_input_adapter object for the target array. template -inline auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + (N - 1))) { - return input_adapter(array, array + (N - 1)); +inline auto input_adapter(T (&array)[N]) -> decltype(create_iterator_input_adapter(array, array + (N - 1), true)) { + return create_iterator_input_adapter(array, array + (N - 1), true); } /// @brief A namespace to implement container_input_adapter_factory for internal use. @@ -6645,15 +8928,18 @@ struct container_input_adapter_factory {}; template struct container_input_adapter_factory< ContainerType, void_t()), end(std::declval()))>> { + /// Whether or not ContainerType is a contiguous container. + static constexpr bool is_contiguous = is_contiguous_container::value; + /// A type for resulting input adapter object. - using adapter_type = - decltype(input_adapter(begin(std::declval()), end(std::declval()))); + using adapter_type = decltype(create_iterator_input_adapter( + begin(std::declval()), end(std::declval()), is_contiguous)); /// @brief A factory method of input adapter objects for the target container objects. /// @param container A container-like input object. /// @return adapter_type An iterator_input_adapter object. static adapter_type create(const ContainerType& container) { - return input_adapter(begin(container), end(container)); + return create_iterator_input_adapter(begin(container), end(container), is_contiguous); } }; @@ -6673,10 +8959,10 @@ inline typename input_adapter_factory::container_input_adapter_factory /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -6851,7 +9137,7 @@ class iterator { /// @param rhs An iterator object to be copied with. /// @return iterator& Reference to this iterator object. iterator& operator=(const iterator& rhs) noexcept { - if (&rhs == this) { + if FK_YAML_UNLIKELY (&rhs == this) { return *this; } @@ -6872,7 +9158,7 @@ class iterator { /// @param rhs An iterator object to be moved from. /// @return iterator& Reference to this iterator object. iterator& operator=(iterator&& rhs) noexcept { - if (&rhs == this) { + if FK_YAML_UNLIKELY (&rhs == this) { return *this; } @@ -7000,7 +9286,7 @@ class iterator { /// @return true This iterator object is equal to the other. /// @return false This iterator object is not equal to the other. bool operator==(const iterator& rhs) const { - if (m_inner_iterator_type != rhs.m_inner_iterator_type) { + if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) { throw fkyaml::exception("Cannot compare iterators of different container types."); } @@ -7025,11 +9311,11 @@ class iterator { /// @return true This iterator object is less than the other. /// @return false This iterator object is not less than the other. bool operator<(const iterator& rhs) const { - if (m_inner_iterator_type != rhs.m_inner_iterator_type) { + if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) { throw fkyaml::exception("Cannot compare iterators of different container types."); } - if (m_inner_iterator_type == iterator_t::MAPPING) { + if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::MAPPING) { throw fkyaml::exception("Cannot compare order of iterators of the mapping container type"); } @@ -7070,7 +9356,7 @@ class iterator { /// @brief Get the key string of the YAML mapping node for the current iterator. /// @return const std::string& The key string of the YAML mapping node for the current iterator. const typename ValueType::mapping_type::key_type& key() const { - if (m_inner_iterator_type == iterator_t::SEQUENCE) { + if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::SEQUENCE) { throw fkyaml::exception("Cannot retrieve key from non-mapping iterators."); } @@ -7100,12 +9386,14 @@ FK_YAML_DETAIL_NAMESPACE_END // #include +// #include + // #include // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7198,7 +9486,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7212,13 +9500,14 @@ FK_YAML_DETAIL_NAMESPACE_END #include #include #include +#include // #include // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7309,17 +9598,15 @@ FK_YAML_DETAIL_NAMESPACE_END // #include -// #include - -// #include +// #include // #include -// #include +// #include -// #include +// #include -// #include +// #include FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -7387,10 +9674,10 @@ class basic_serializer { if (p_meta->is_version_specified) { str += "%YAML "; switch (p_meta->version) { - case yaml_version_t::VER_1_1: + case yaml_version_type::VERSION_1_1: str += "1.1\n"; break; - case yaml_version_t::VER_1_2: + case yaml_version_type::VERSION_1_2: str += "1.2\n"; break; } @@ -7434,8 +9721,8 @@ class basic_serializer { /// @param cur_indent The current indent width /// @param str A string to hold serialization result. void serialize_node(const BasicNodeType& node, const uint32_t cur_indent, std::string& str) { - switch (node.type()) { - case node_t::SEQUENCE: + switch (node.get_type()) { + case node_type::SEQUENCE: for (const auto& seq_item : node) { insert_indentation(cur_indent, str); str += "-"; @@ -7461,7 +9748,7 @@ class basic_serializer { } } break; - case node_t::MAPPING: + case node_type::MAPPING: for (auto itr = node.begin(); itr != node.end(); ++itr) { insert_indentation(cur_indent, str); @@ -7512,23 +9799,23 @@ class basic_serializer { } } break; - case node_t::NULL_OBJECT: + case node_type::NULL_OBJECT: to_string(nullptr, m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::BOOLEAN: + case node_type::BOOLEAN: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::INTEGER: + case node_type::INTEGER: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::FLOAT_NUMBER: + case node_type::FLOAT: to_string(node.template get_value(), m_tmp_str_buff); str += m_tmp_str_buff; break; - case node_t::STRING: { + case node_type::STRING: { bool is_escaped = false; typename BasicNodeType::string_type str_val = get_string_node_value(node, is_escaped); @@ -7541,11 +9828,13 @@ class basic_serializer { break; } - auto adapter = input_adapter(str_val); - lexical_analyzer lexer(std::move(adapter)); - lexical_token_t token_type = lexer.get_next_token(); + // The next line is intentionally excluded from the LCOV coverage target since the next line is somehow + // misrecognized as it has a binary branch. Possibly begin() or end() has some conditional branch(es) + // internally. Confirmed with LCOV 1.14 on Ubuntu22.04. + node_type type_if_plain = + scalar_scanner::scan(str_val.c_str(), str_val.c_str() + str_val.size()); // LCOV_EXCL_LINE - if (token_type != lexical_token_t::STRING_VALUE) { + if (type_if_plain != node_type::STRING) { // Surround a string value with double quotes to keep semantic equality. // Without them, serialized values will become non-string. (e.g., "1" -> 1) str += '\"'; @@ -7640,7 +9929,7 @@ class basic_serializer { using string_type = typename BasicNodeType::string_type; const string_type& s = node.template get_value_ref(); - return yaml_escaper::escape(s.begin(), s.end(), is_escaped); + return yaml_escaper::escape(s.c_str(), s.c_str() + s.size(), is_escaped); } // LCOV_EXCL_LINE private: @@ -7654,12 +9943,69 @@ FK_YAML_DETAIL_NAMESPACE_END // #include +// #include +/// _______ __ __ __ _____ __ __ __ +/// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +/// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +/// +/// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +/// SPDX-License-Identifier: MIT +/// +/// @file + +#ifndef FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ +#define FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ + +#include + +// #include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Definition of YAML version types. +enum class yaml_version_t : std::uint32_t { + VER_1_1, //!< YAML version 1.1 + VER_1_2, //!< YAML version 1.2 +}; + +inline yaml_version_t convert_from_yaml_version_type(yaml_version_type t) noexcept { + switch (t) { + case yaml_version_type::VERSION_1_1: + return yaml_version_t::VER_1_1; + case yaml_version_type::VERSION_1_2: + return yaml_version_t::VER_1_2; + default: // LCOV_EXCL_LINE + return yaml_version_t::VER_1_2; // LCOV_EXCL_LINE + } +} + +inline yaml_version_type convert_to_yaml_version_type(yaml_version_t t) noexcept { + switch (t) { + case yaml_version_t::VER_1_1: + return yaml_version_type::VERSION_1_1; + case yaml_version_t::VER_1_2: + return yaml_version_type::VERSION_1_2; + default: // LCOV_EXCL_LINE + return yaml_version_type::VERSION_1_2; // LCOV_EXCL_LINE + } +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP_ */ + // #include +// #include + // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7677,7 +10023,7 @@ FK_YAML_DETAIL_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7718,8 +10064,8 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @param s A sequence node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::sequence_type& s) { - if (!n.is_sequence()) { - throw type_error("The target node value type is not sequence type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value type is not sequence type.", n.get_type()); } s = n.template get_value_ref(); } @@ -7738,8 +10084,8 @@ template < negation, typename BasicNodeType::sequence_type>>>::value, int> = 0> inline void from_node(const BasicNodeType& n, std::vector& s) { - if (!n.is_sequence()) { - throw type_error("The target node value is not sequence type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_sequence()) { + throw type_error("The target node value is not sequence type.", n.get_type()); } s.reserve(n.size()); @@ -7755,8 +10101,8 @@ inline void from_node(const BasicNodeType& n, std::vector& /// @param m A mapping node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::mapping_type& m) { - if (!n.is_mapping()) { - throw type_error("The target node value type is not mapping type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_mapping()) { + throw type_error("The target node value type is not mapping type.", n.get_type()); } for (auto pair : n.template get_value_ref()) { @@ -7774,8 +10120,8 @@ template < has_from_node>::value, int> = 0> inline void from_node(const BasicNodeType& n, std::map& m) { - if (!n.is_mapping()) { - throw type_error("The target node value type is not mapping type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_mapping()) { + throw type_error("The target node value type is not mapping type.", n.get_type()); } for (auto pair : n.template get_value_ref()) { @@ -7791,8 +10137,8 @@ inline void from_node(const BasicNodeType& n, std::map::value, int> = 0> inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { // to ensure the target node value type is null. - if (!n.is_null()) { - throw type_error("The target node value type is not null type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_null()) { + throw type_error("The target node value type is not null type.", n.get_type()); } null = nullptr; } @@ -7803,8 +10149,8 @@ inline void from_node(const BasicNodeType& n, std::nullptr_t& null) { /// @param b A boolean node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_type& b) { - if (!n.is_boolean()) { - throw type_error("The target node value type is not boolean type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_boolean()) { + throw type_error("The target node value type is not boolean type.", n.get_type()); } b = n.template get_value_ref(); } @@ -7815,8 +10161,8 @@ inline void from_node(const BasicNodeType& n, typename BasicNodeType::boolean_ty /// @param i An integer node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::integer_type& i) { - if (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw type_error("The target node value type is not integer type.", n.get_type()); } i = n.template get_value_ref(); } @@ -7834,17 +10180,17 @@ template < negation>>::value, int> = 0> inline void from_node(const BasicNodeType& n, IntegerType& i) { - if (!n.is_integer()) { - throw type_error("The target node value type is not integer type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw type_error("The target node value type is not integer type.", n.get_type()); } // under/overflow check. using node_int_type = typename BasicNodeType::integer_type; node_int_type tmp_int = n.template get_value_ref(); - if (tmp_int < static_cast(std::numeric_limits::min())) { + if FK_YAML_UNLIKELY (tmp_int < static_cast(std::numeric_limits::min())) { throw exception("Integer value underflow detected."); } - if (static_cast(std::numeric_limits::max()) < tmp_int) { + if FK_YAML_UNLIKELY (static_cast(std::numeric_limits::max()) < tmp_int) { throw exception("Integer value overflow detected."); } @@ -7857,8 +10203,8 @@ inline void from_node(const BasicNodeType& n, IntegerType& i) { /// @param f A float number node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::float_number_type& f) { - if (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_float_number()) { + throw type_error("The target node value type is not float number type.", n.get_type()); } f = n.template get_value_ref(); } @@ -7876,15 +10222,15 @@ template < negation>>::value, int> = 0> inline void from_node(const BasicNodeType& n, FloatType& f) { - if (!n.is_float_number()) { - throw type_error("The target node value type is not float number type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_float_number()) { + throw type_error("The target node value type is not float number type.", n.get_type()); } auto tmp_float = n.template get_value_ref(); - if (tmp_float < std::numeric_limits::lowest()) { + if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits::lowest()) { throw exception("Floating point value underflow detected."); } - if (std::numeric_limits::max() < tmp_float) { + if FK_YAML_UNLIKELY (std::numeric_limits::max() < tmp_float) { throw exception("Floating point value overflow detected."); } @@ -7897,8 +10243,8 @@ inline void from_node(const BasicNodeType& n, FloatType& f) { /// @param s A string node value object. template ::value, int> = 0> inline void from_node(const BasicNodeType& n, typename BasicNodeType::string_type& s) { - if (!n.is_string()) { - throw type_error("The target node value type is not string type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_string()) { + throw type_error("The target node value type is not string type.", n.get_type()); } s = n.template get_value_ref(); } @@ -7919,8 +10265,8 @@ template < std::is_assignable>>::value, int> = 0> inline void from_node(const BasicNodeType& n, CompatibleStringType& s) { - if (!n.is_string()) { - throw type_error("The target node value type is not string type.", n.type()); + if FK_YAML_UNLIKELY (!n.is_string()) { + throw type_error("The target node value type is not string type.", n.get_type()); } s = n.template get_value_ref(); } @@ -7967,7 +10313,7 @@ FK_YAML_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -7982,14 +10328,16 @@ FK_YAML_NAMESPACE_END // #include -// #include - // #include // #include // #include +// #include + +// #include + FK_YAML_DETAIL_NAMESPACE_BEGIN @@ -8000,23 +10348,23 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @brief The external constructor template for basic_node objects. /// @note All the non-specialized instanciations results in compilation error since such instantiations are not /// supported. -/// @warning All the specialization must call n.m_node_value.destroy(n.m_node_type) first in construct function to avoid +/// @warning All the specialization must call n.m_node_value.destroy() first in the construct function to avoid /// memory leak. -/// @tparam node_t The resulting YAMK node value type. -template +/// @tparam node_type The resulting YAMK node value type. +template struct external_node_constructor; /// @brief The specialization of external_node_constructor for sequence nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue sequence. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param s A lvalue sequence value. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::sequence_type& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::SEQUENCE; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::seq_bit; n.m_node_value.p_sequence = BasicNodeType::template create_object(s); } @@ -8026,8 +10374,8 @@ struct external_node_constructor { /// @param s A rvalue sequence value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::sequence_type&& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::SEQUENCE; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::seq_bit; n.m_node_value.p_sequence = BasicNodeType::template create_object(std::move(s)); } @@ -8035,15 +10383,15 @@ struct external_node_constructor { /// @brief The specialization of external_node_constructor for mapping nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue mapping. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param m A lvalue mapping value. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::mapping_type& m) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::MAPPING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::map_bit; n.m_node_value.p_mapping = BasicNodeType::template create_object(m); } @@ -8053,8 +10401,8 @@ struct external_node_constructor { /// @param m A rvalue mapping value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::mapping_type&& m) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::MAPPING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::map_bit; n.m_node_value.p_mapping = BasicNodeType::template create_object(std::move(m)); } @@ -8062,75 +10410,75 @@ struct external_node_constructor { /// @brief The specialization of external_node_constructor for null nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with nullptr. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param (unused) nullptr template ::value, int> = 0> static void construct(BasicNodeType& n, std::nullptr_t /*unused*/) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::NULL_OBJECT; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::null_bit; n.m_node_value.p_mapping = nullptr; } }; /// @brief The specialization of external_node_constructor for boolean scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with boolean. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param b A boolean value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::BOOLEAN; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::bool_bit; n.m_node_value.boolean = b; } }; /// @brief The specialization of external_node_constructor for integer scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with integers. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param i An integer value. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::integer_type i) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::INTEGER; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::int_bit; n.m_node_value.integer = i; } }; /// @brief The specialization of external_node_constructor for float number scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with floating point numbers. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param f A floating point number. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::float_number_type f) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::FLOAT_NUMBER; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::float_bit; n.m_node_value.float_val = f; } }; /// @brief The specialization of external_node_constructor for string scalar nodes. template <> -struct external_node_constructor { +struct external_node_constructor { /// @brief Constructs a basic_node object with const lvalue strings. /// @tparam BasicNodeType A basic_node template instance type. /// @param n A basic_node object. /// @param s A constant lvalue string. template ::value, int> = 0> static void construct(BasicNodeType& n, const typename BasicNodeType::string_type& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(s); } @@ -8140,8 +10488,8 @@ struct external_node_constructor { /// @param s A rvalue string. template ::value, int> = 0> static void construct(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(std::move(s)); } @@ -8159,8 +10507,8 @@ struct external_node_constructor { negation>>::value, int> = 0> static void construct(BasicNodeType& n, const CompatibleStringType& s) noexcept { - n.m_node_value.destroy(n.m_node_type); - n.m_node_type = node_t::STRING; + n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); + n.m_attrs = detail::node_attr_bits::string_bit; n.m_node_value.p_string = BasicNodeType::template create_object(s); } }; @@ -8182,7 +10530,7 @@ template < std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& s) noexcept { - external_node_constructor::construct(n, std::forward(s)); + external_node_constructor::construct(n, std::forward(s)); } /// @brief to_node function for BasicNodeType::mapping_type objects. @@ -8197,7 +10545,7 @@ template < is_basic_node, std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& m) noexcept { - external_node_constructor::construct(n, std::forward(m)); + external_node_constructor::construct(n, std::forward(m)); } /// @brief to_node function for null objects. @@ -8207,7 +10555,7 @@ template < typename BasicNodeType, typename NullType, enable_if_t, std::is_same>::value, int> = 0> inline void to_node(BasicNodeType& n, NullType /*unused*/) { - external_node_constructor::construct(n, nullptr); + external_node_constructor::construct(n, nullptr); } /// @brief to_node function for BasicNodeType::boolean_type objects. @@ -8221,7 +10569,7 @@ template < conjunction, std::is_same>::value, int> = 0> inline void to_node(BasicNodeType& n, T b) noexcept { - external_node_constructor::construct(n, b); + external_node_constructor::construct(n, b); } /// @brief to_node function for integers. @@ -8233,7 +10581,7 @@ template < typename BasicNodeType, typename T, enable_if_t, is_non_bool_integral>::value, int> = 0> inline void to_node(BasicNodeType& n, T i) noexcept { - external_node_constructor::construct(n, i); + external_node_constructor::construct(n, i); } /// @brief to_node function for floating point numbers. @@ -8245,7 +10593,7 @@ template < typename BasicNodeType, typename T, enable_if_t, std::is_floating_point>::value, int> = 0> inline void to_node(BasicNodeType& n, T f) noexcept { - external_node_constructor::construct(n, f); + external_node_constructor::construct(n, f); } /// @brief to_node function for compatible strings. @@ -8261,7 +10609,7 @@ template < std::is_constructible>::value, int> = 0> inline void to_node(BasicNodeType& n, const T& s) { - external_node_constructor::construct(n, s); + external_node_constructor::construct(n, s); } /// @brief to_node function for rvalue string node values @@ -8270,7 +10618,7 @@ inline void to_node(BasicNodeType& n, const T& s) { /// @param s An rvalue string node value. template ::value, int> = 0> inline void to_node(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - external_node_constructor::construct(n, std::move(s)); + external_node_constructor::construct(n, std::move(s)); } /// @brief A function object to call to_node functions. @@ -8355,7 +10703,7 @@ FK_YAML_NAMESPACE_END // #include /// _______ __ __ __ _____ __ __ __ /// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library -/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +/// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 /// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML /// /// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -8584,15 +10932,17 @@ class basic_node { using value_converter_type = ConverterType; /// @brief Definition of node value types. + /// @deprecated Use fkyaml::node_type enum class. (since 0.3.12) /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/node_t/ using node_t = detail::node_t; /// @brief Definition of YAML version types. + /// @deprecated Use fkyaml::yaml_version_type enum class. (since 0.3.12) /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/yaml_version_t/ using yaml_version_t = detail::yaml_version_t; private: - template + template friend struct fkyaml::detail::external_node_constructor; template @@ -8619,48 +10969,50 @@ class basic_node { /// @brief Constructs a new basic_node value object with a node type. The default value for the specified /// type will be assigned. /// @param[in] type A node type. - explicit node_value(node_t type) { - switch (type) { - case node_t::SEQUENCE: + explicit node_value(detail::node_attr_t value_type_bit) { + switch (value_type_bit) { + case detail::node_attr_bits::seq_bit: p_sequence = create_object(); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: p_mapping = create_object(); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: p_mapping = nullptr; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: boolean = static_cast(false); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: integer = static_cast(0); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: float_val = static_cast(0.0); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: p_string = create_object(); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } /// @brief Destroys the existing Node value. This process is recursive if the specified node type is for /// containers. /// @param[in] type A Node type to determine the value to be destroyed. - void destroy(node_t type) { - switch (type) { - case node_t::SEQUENCE: + void destroy(detail::node_attr_t value_type_bit) { + switch (value_type_bit) { + case detail::node_attr_bits::seq_bit: p_sequence->clear(); destroy_object(p_sequence); p_sequence = nullptr; break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: p_mapping->clear(); destroy_object(p_mapping); p_mapping = nullptr; break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: destroy_object(p_string); p_string = nullptr; break; @@ -8727,41 +11079,48 @@ class basic_node { /// @brief Constructs a new basic_node object with a specified type. /// @param[in] type A YAML node type. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ + FK_YAML_DEPRECATED("Since 0.3.12; Use explicit basic_node(const node_type)") explicit basic_node(const node_t type) - : m_node_type(type), - m_node_value(type) { + : basic_node(detail::convert_to_node_type(type)) { + } + + explicit basic_node(const node_type type) + : m_attrs(detail::node_attr_bits::from_node_type(type)), + m_node_value(m_attrs & detail::node_attr_mask::value) { } /// @brief Copy constructor of the basic_node class. /// @param[in] rhs A basic_node object to be copied with. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ basic_node(const basic_node& rhs) - : m_node_type(rhs.m_node_type), + : m_attrs(rhs.m_attrs), mp_meta(rhs.mp_meta), m_prop(rhs.m_prop) { - if (!has_anchor_name()) { - switch (m_node_type) { - case node_t::SEQUENCE: + if FK_YAML_LIKELY (!has_anchor_name()) { + switch (m_attrs & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: m_node_value.p_sequence = create_object(*(rhs.m_node_value.p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: m_node_value.p_mapping = create_object(*(rhs.m_node_value.p_mapping)); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: m_node_value.p_mapping = nullptr; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: m_node_value.boolean = rhs.m_node_value.boolean; break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: m_node_value.integer = rhs.m_node_value.integer; break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: m_node_value.float_val = rhs.m_node_value.float_val; break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: m_node_value.p_string = create_object(*(rhs.m_node_value.p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } } @@ -8770,48 +11129,49 @@ class basic_node { /// @param[in] rhs A basic_node object to be moved from. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ basic_node(basic_node&& rhs) noexcept - : m_node_type(rhs.m_node_type), + : m_attrs(rhs.m_attrs), mp_meta(std::move(rhs.mp_meta)), m_prop(std::move(rhs.m_prop)) { - if (!has_anchor_name()) { - switch (m_node_type) { - case node_t::SEQUENCE: + if FK_YAML_LIKELY (!has_anchor_name()) { + switch (m_attrs & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: FK_YAML_ASSERT(rhs.m_node_value.p_sequence != nullptr); m_node_value.p_sequence = rhs.m_node_value.p_sequence; rhs.m_node_value.p_sequence = nullptr; break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: FK_YAML_ASSERT(rhs.m_node_value.p_mapping != nullptr); m_node_value.p_mapping = rhs.m_node_value.p_mapping; rhs.m_node_value.p_mapping = nullptr; break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: FK_YAML_ASSERT(rhs.m_node_value.p_mapping == nullptr); m_node_value.p_mapping = rhs.m_node_value.p_mapping; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: m_node_value.boolean = rhs.m_node_value.boolean; rhs.m_node_value.boolean = static_cast(false); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: m_node_value.integer = rhs.m_node_value.integer; rhs.m_node_value.integer = static_cast(0); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: m_node_value.float_val = rhs.m_node_value.float_val; rhs.m_node_value.float_val = static_cast(0.0); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: FK_YAML_ASSERT(rhs.m_node_value.p_string != nullptr); m_node_value.p_string = rhs.m_node_value.p_string; rhs.m_node_value.p_string = nullptr; break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } - rhs.m_node_type = node_t::NULL_OBJECT; + rhs.m_attrs = detail::node_attr_bits::default_bits; rhs.m_node_value.p_mapping = nullptr; - rhs.m_prop.anchor_status = detail::anchor_status_t::NONE; } /// @brief Construct a new basic_node object from a value of compatible types. @@ -8852,7 +11212,7 @@ class basic_node { }); if (is_mapping) { - m_node_type = node_t::MAPPING; + m_attrs = detail::node_attr_bits::map_bit; m_node_value.p_mapping = create_object(); for (auto& elem_ref : init) { @@ -8862,7 +11222,7 @@ class basic_node { } } else { - m_node_type = node_t::SEQUENCE; + m_attrs = detail::node_attr_bits::seq_bit; m_node_value.p_sequence = create_object(); m_node_value.p_sequence->reserve(std::distance(init.begin(), init.end())); for (auto& elem_ref : init) { @@ -8875,24 +11235,20 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/destructor/ ~basic_node() noexcept // NOLINT(bugprone-exception-escape) { - switch (m_prop.anchor_status) { - case detail::anchor_status_t::NONE: - if (m_node_type != node_t::NULL_OBJECT) { - m_node_value.destroy(m_node_type); + if (m_attrs & detail::node_attr_mask::anchoring) { + if (m_attrs & detail::node_attr_bits::anchor_bit) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + itr->second.m_node_value.destroy(itr->second.m_attrs & detail::node_attr_mask::value); + itr->second.m_attrs = detail::node_attr_bits::default_bits; + itr->second.mp_meta.reset(); } - break; - case detail::anchor_status_t::ANCHOR: { - auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); - itr->second.m_node_value.destroy(itr->second.m_node_type); - itr->second.m_node_type = node_t::NULL_OBJECT; - itr->second.mp_meta.reset(); - break; } - case detail::anchor_status_t::ALIAS: - break; + else if ((m_attrs & detail::node_attr_bits::null_bit) == 0) { + m_node_value.destroy(m_attrs & detail::node_attr_mask::value); } - m_node_type = node_t::NULL_OBJECT; + + m_attrs = detail::node_attr_bits::default_bits; mp_meta.reset(); } @@ -8962,7 +11318,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence() { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(); return node; } // LCOV_EXCL_LINE @@ -8973,7 +11329,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence(const sequence_type& seq) { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(seq); return node; } // LCOV_EXCL_LINE @@ -8984,7 +11340,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/ static basic_node sequence(sequence_type&& seq) { basic_node node; - node.m_node_type = node_t::SEQUENCE; + node.m_attrs = detail::node_attr_bits::seq_bit; node.m_node_value.p_sequence = create_object(std::move(seq)); return node; } // LCOV_EXCL_LINE @@ -8994,7 +11350,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping() { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(); return node; } // LCOV_EXCL_LINE @@ -9005,7 +11361,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping(const mapping_type& map) { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(map); return node; } // LCOV_EXCL_LINE @@ -9016,7 +11372,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/ static basic_node mapping(mapping_type&& map) { basic_node node; - node.m_node_type = node_t::MAPPING; + node.m_attrs = detail::node_attr_bits::map_bit; node.m_node_value.p_mapping = create_object(std::move(map)); return node; } // LCOV_EXCL_LINE @@ -9027,12 +11383,15 @@ class basic_node { /// @return An alias YAML node created from the given anchor node. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/alias_of/ static basic_node alias_of(const basic_node& anchor_node) { - if (!anchor_node.has_anchor_name() || anchor_node.m_prop.anchor_status != detail::anchor_status_t::ANCHOR) { + using namespace detail::node_attr_bits; + + if FK_YAML_UNLIKELY (!anchor_node.has_anchor_name() || !(anchor_node.m_attrs & anchor_bit)) { throw fkyaml::exception("Cannot create an alias without anchor name."); } basic_node node = anchor_node; - node.m_prop.anchor_status = detail::anchor_status_t::ALIAS; + node.m_attrs &= ~detail::node_attr_mask::anchoring; + node.m_attrs |= alias_bit; return node; } // LCOV_EXCL_LINE @@ -9067,16 +11426,17 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> basic_node& operator[](KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } basic_node n = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!n.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!n.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](n.get_value()); @@ -9098,16 +11458,17 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> const basic_node& operator[](KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](node_key.get_value()); @@ -9125,15 +11486,16 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> basic_node& operator[](KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](key.template get_value()); @@ -9151,15 +11513,16 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> const basic_node& operator[](KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("operator[] is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of operator[] for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error( + "An argument of operator[] for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->operator[](key.template get_value()); @@ -9174,40 +11537,42 @@ class basic_node { /// @return true if both types and values are equal, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_eq/ bool operator==(const basic_node& rhs) const noexcept { - node_t this_type = type(); - const node_value* this_node_value_ptr = get_node_value_ptr(); - const node_value* other_node_value_ptr = rhs.get_node_value_ptr(); - - if (this_type != rhs.type()) { + detail::node_attr_t this_val_bit = get_node_attrs() & detail::node_attr_mask::value; + if (this_val_bit != (rhs.get_node_attrs() & detail::node_attr_mask::value)) { return false; } + const node_value* this_node_value_ptr = get_node_value_ptr(); + const node_value* other_node_value_ptr = rhs.get_node_value_ptr(); + bool ret = false; - switch (this_type) { - case node_t::SEQUENCE: + switch (this_val_bit) { + case detail::node_attr_bits::seq_bit: ret = (*(this_node_value_ptr->p_sequence) == *(other_node_value_ptr->p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: ret = (*(this_node_value_ptr->p_mapping) == *(other_node_value_ptr->p_mapping)); break; - case node_t::NULL_OBJECT: + case detail::node_attr_bits::null_bit: // Always true for comparisons between null nodes. ret = true; break; - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: ret = (this_node_value_ptr->boolean == other_node_value_ptr->boolean); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: ret = (this_node_value_ptr->integer == other_node_value_ptr->integer); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: ret = (std::abs(this_node_value_ptr->float_val - other_node_value_ptr->float_val) < std::numeric_limits::epsilon()); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: ret = (*(this_node_value_ptr->p_string) == *(other_node_value_ptr->p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } return ret; @@ -9230,14 +11595,14 @@ class basic_node { return false; } - node_t this_type = type(); - node_t other_type = rhs.type(); + detail::node_attr_t this_val_bit = get_node_attrs() & detail::node_attr_mask::value; + detail::node_attr_t other_val_bit = rhs.get_node_attrs() & detail::node_attr_mask::value; - if (static_cast(this_type) < static_cast(other_type)) { + if (this_val_bit < other_val_bit) { return true; } - if (this_type != other_type) { + if (this_val_bit != other_val_bit) { return false; } @@ -9245,29 +11610,31 @@ class basic_node { const node_value* p_other_value = rhs.get_node_value_ptr(); bool ret = false; - switch (this_type) { - case node_t::SEQUENCE: + switch (this_val_bit) { + case detail::node_attr_bits::seq_bit: ret = (*(p_this_value->p_sequence) < *(p_other_value->p_sequence)); break; - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: ret = (*(p_this_value->p_mapping) < *(p_other_value->p_mapping)); break; - case node_t::NULL_OBJECT: // LCOV_EXCL_LINE + case detail::node_attr_bits::null_bit: // LCOV_EXCL_LINE // Will not come here since null nodes are alyways the same. break; // LCOV_EXCL_LINE - case node_t::BOOLEAN: + case detail::node_attr_bits::bool_bit: // false < true ret = (!p_this_value->boolean && p_other_value->boolean); break; - case node_t::INTEGER: + case detail::node_attr_bits::int_bit: ret = (p_this_value->integer < p_other_value->integer); break; - case node_t::FLOAT_NUMBER: + case detail::node_attr_bits::float_bit: ret = (p_this_value->float_val < p_other_value->float_val); break; - case node_t::STRING: + case detail::node_attr_bits::string_bit: ret = (*(p_this_value->p_string) < *(p_other_value->p_string)); break; + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } return ret; @@ -9300,108 +11667,114 @@ class basic_node { public: /// @brief Returns the type of the current basic_node value. /// @return The type of the YAML node value. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_type/ + node_type get_type() const noexcept { + detail::node_attr_t attrs = get_node_attrs(); + return detail::node_attr_bits::to_node_type(attrs); + } + + /// @brief Returns the type of the current basic_node value. + /// @deprecated Use get_type() function. (since 0.3.12) + /// @return The type of the YAML node value. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/type/ + FK_YAML_DEPRECATED("Since 0.3.12; Use get_type()") node_t type() const noexcept { - if (has_anchor_name()) { - auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); - return itr->second.m_node_type; - } - return m_node_type; + node_type tmp_type = get_type(); + return detail::convert_from_node_type(tmp_type); } /// @brief Tests whether the current basic_node value is of sequence type. /// @return true if the type is sequence, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_sequence/ bool is_sequence() const noexcept { - return type() == node_t::SEQUENCE; + return get_node_attrs() & detail::node_attr_bits::seq_bit; } /// @brief Tests whether the current basic_node value is of mapping type. /// @return true if the type is mapping, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_mapping/ bool is_mapping() const noexcept { - return type() == node_t::MAPPING; + return get_node_attrs() & detail::node_attr_bits::map_bit; } /// @brief Tests whether the current basic_node value is of null type. /// @return true if the type is null, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_null/ bool is_null() const noexcept { - return type() == node_t::NULL_OBJECT; + return get_node_attrs() & detail::node_attr_bits::null_bit; } /// @brief Tests whether the current basic_node value is of boolean type. /// @return true if the type is boolean, false otherwise /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_boolean/ bool is_boolean() const noexcept { - return type() == node_t::BOOLEAN; + return get_node_attrs() & detail::node_attr_bits::bool_bit; } /// @brief Tests whether the current basic_node value is of integer type. /// @return true if the type is integer, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_integer/ bool is_integer() const noexcept { - return type() == node_t::INTEGER; + return get_node_attrs() & detail::node_attr_bits::int_bit; } /// @brief Tests whether the current basic_node value is of float number type. /// @return true if the type is floating point number, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_float_number/ bool is_float_number() const noexcept { - return type() == node_t::FLOAT_NUMBER; + return get_node_attrs() & detail::node_attr_bits::float_bit; } /// @brief Tests whether the current basic_node value is of string type. /// @return true if the type is string, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_string/ bool is_string() const noexcept { - return type() == node_t::STRING; + return get_node_attrs() & detail::node_attr_bits::string_bit; } /// @brief Tests whether the current basic_node value is of scalar types. /// @return true if the type is scalar, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_scalar/ bool is_scalar() const noexcept { - return !is_sequence() && !is_mapping(); + return get_node_attrs() & detail::node_attr_bits::scalar_bits; } /// @brief Tests whether the current basic_node is an anchor node. /// @return true if the current basic_node is an anchor node, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_anchor/ bool is_anchor() const noexcept { - return m_prop.anchor_status == detail::anchor_status_t::ANCHOR; + return m_attrs & detail::node_attr_bits::anchor_bit; } /// @brief Tests whether the current basic_node is an alias node. /// @return true if the current basic_node is an alias node, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_alias/ bool is_alias() const noexcept { - return m_prop.anchor_status == detail::anchor_status_t::ALIAS; + return m_attrs & detail::node_attr_bits::alias_bit; } /// @brief Tests whether the current basic_node value (sequence, mapping, string) is empty. /// @return true if the node value is empty, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/empty/ bool empty() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->empty(); } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return p_node_value->p_mapping->empty(); } - case node_t::STRING: { + case detail::node_attr_bits::string_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_string != nullptr); return p_node_value->p_string->empty(); } default: - throw fkyaml::type_error("The target node is not of a container type.", type()); + throw fkyaml::type_error("The target node is not of a container type.", get_type()); } } @@ -9410,18 +11783,18 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/size/ std::size_t size() const { const node_value* p_node_value = get_node_value_ptr(); - switch (type()) { - case node_t::SEQUENCE: + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return p_node_value->p_sequence->size(); - case node_t::MAPPING: + case detail::node_attr_bits::map_bit: FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return p_node_value->p_mapping->size(); - case node_t::STRING: + case detail::node_attr_bits::string_bit: FK_YAML_ASSERT(p_node_value->p_string != nullptr); return p_node_value->p_string->size(); default: - throw fkyaml::type_error("The target node is not of a container type.", type()); + throw fkyaml::type_error("The target node is not of a container type.", get_type()); } } @@ -9437,18 +11810,16 @@ class basic_node { detail::is_node_compatible_type>>::value, int> = 0> bool contains(KeyType&& key) const { - switch (type()) { - case node_t::MAPPING: { + if FK_YAML_LIKELY (get_node_attrs() & detail::node_attr_bits::map_bit) { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - mapping_type& map = *p_node_value->p_mapping; + const mapping_type& map = *p_node_value->p_mapping; basic_node node_key = std::forward(key); return map.find(std::move(node_key)) != map.end(); } - default: - return false; - } + + return false; } /// @brief Check whether or not this basic_node object has a given key in its inner mapping Node value. @@ -9459,17 +11830,15 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> bool contains(KeyType&& key) const { - switch (type()) { - case node_t::MAPPING: { + if FK_YAML_LIKELY (get_node_attrs() & detail::node_attr_bits::map_bit) { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - mapping_type& map = *p_node_value->p_mapping; + const mapping_type& map = *p_node_value->p_mapping; return map.find(std::forward(key)) != map.end(); } - default: - return false; - } + + return false; } /// @brief Get a basic_node object with a key of a compatible type. @@ -9484,33 +11853,35 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> basic_node& at(KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + sequence_type& seq = *p_node_value->p_sequence; int index = node_key.template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(node_key) != p_node_value->p_mapping->end(); - if (!is_found) { + mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(node_key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(node_key).c_str()); } - return p_node_value->p_mapping->at(node_key); + return map.at(node_key); } /// @brief Get a basic_node object with a key of a compatible type. @@ -9525,33 +11896,35 @@ class basic_node { detail::is_node_compatible_type>::value, int> = 0> const basic_node& at(KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } basic_node node_key = std::forward(key); const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!node_key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!node_key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + const sequence_type& seq = *p_node_value->p_sequence; int index = node_key.template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(node_key) != p_node_value->p_mapping->end(); - if (!is_found) { + const mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(node_key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(node_key).c_str()); } - return p_node_value->p_mapping->at(node_key); + return map.at(node_key); } /// @brief Get a basic_node object with a basic_node key object. @@ -9562,32 +11935,34 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> basic_node& at(KeyType&& key) { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + sequence_type& seq = *p_node_value->p_sequence; int index = std::forward(key).template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(key) != p_node_value->p_mapping->end(); - if (!is_found) { + mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(key).c_str()); } - return p_node_value->p_mapping->at(key); + return map.at(key); } /// @brief Get a basic_node object with a basic_node key object. @@ -9598,55 +11973,75 @@ class basic_node { template < typename KeyType, detail::enable_if_t>::value, int> = 0> const basic_node& at(KeyType&& key) const { - if (is_scalar()) { - throw fkyaml::type_error("at() is unavailable for a scalar node.", type()); + if FK_YAML_UNLIKELY (is_scalar()) { + throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type()); } const node_value* p_node_value = get_node_value_ptr(); if (is_sequence()) { - if (!key.is_integer()) { - throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", type()); + if FK_YAML_UNLIKELY (!key.is_integer()) { + throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type()); } FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); + const sequence_type& seq = *p_node_value->p_sequence; int index = std::forward(key).template get_value(); - int size = static_cast(p_node_value->p_sequence->size()); - if (index >= size) { + int size = static_cast(seq.size()); + if FK_YAML_UNLIKELY (index >= size) { throw fkyaml::out_of_range(index); } - return p_node_value->p_sequence->at(index); + return seq.at(index); } FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); - bool is_found = p_node_value->p_mapping->find(key) != p_node_value->p_mapping->end(); - if (!is_found) { + const mapping_type& map = *p_node_value->p_mapping; + bool is_found = map.find(key) != map.end(); + if FK_YAML_UNLIKELY (!is_found) { throw fkyaml::out_of_range(serialize(key).c_str()); } - return p_node_value->p_mapping->at(key); + return map.at(key); + } + + /// @brief Get the YAML version for this basic_node object. + /// @return The YAML version if already set, `yaml_version_type::VERSION_1_2` otherwise. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version_type/ + yaml_version_type get_yaml_version_type() const noexcept { + return mp_meta->is_version_specified ? mp_meta->version : yaml_version_type::VERSION_1_2; + } + + /// @brief Set the YAML version for this basic_node object. + /// @param[in] version The target YAML version. + /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version_type/ + void set_yaml_version_type(const yaml_version_type version) noexcept { + mp_meta->version = version; + mp_meta->is_version_specified = true; } - /// @brief Get the YAML version specification for this basic_node object. - /// @return The YAML version if any is applied to the basic_node object, `yaml_version_t::VER_1_2` otherwise. + /// @brief Get the YAML version for this basic_node object. + /// @deprecated Use get_yaml_version_type() function. (since 0.3.12) + /// @return The YAML version if already set, `yaml_version_t::VER_1_2` otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version/ + FK_YAML_DEPRECATED("Since 0.3.12; Use get_yaml_version_type()") yaml_version_t get_yaml_version() const noexcept { - return mp_meta->is_version_specified ? mp_meta->version : yaml_version_t::VER_1_2; + yaml_version_type tmp_type = get_yaml_version_type(); + return detail::convert_from_yaml_version_type(tmp_type); } - /// @brief Set the YAML version specification for this basic_node object. - /// @note If no YAML directive - /// @param[in] A version of the YAML format. + /// @brief Set the YAML version for this basic_node object. + /// @deprecated Use set_yaml_version_type(yaml_version_type) function. (since 0.3.12) + /// @param[in] version The target YAML version. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version/ + FK_YAML_DEPRECATED("Since 0.3.12; Use set_yaml_version_type(const yaml_version_type)") void set_yaml_version(const yaml_version_t version) noexcept { - mp_meta->version = version; - mp_meta->is_version_specified = true; + set_yaml_version_type(detail::convert_to_yaml_version_type(version)); } /// @brief Check whether or not this basic_node object has already had any anchor name. /// @return true if ths basic_node has an anchor name, false otherwise. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/has_anchor_name/ bool has_anchor_name() const noexcept { - return m_prop.anchor_status != detail::anchor_status_t::NONE && !m_prop.anchor.empty(); + return (m_attrs & detail::node_attr_mask::anchoring) && !m_prop.anchor.empty(); } /// @brief Get the anchor name associated with this basic_node object. @@ -9655,7 +12050,7 @@ class basic_node { /// @return The anchor name associated with the node. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_anchor_name/ const std::string& get_anchor_name() const { - if (!has_anchor_name()) { + if FK_YAML_UNLIKELY (!has_anchor_name()) { throw fkyaml::exception("No anchor name has been set."); } return m_prop.anchor; @@ -9667,9 +12062,9 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/ void add_anchor_name(const std::string& anchor_name) { if (is_anchor()) { - m_prop.anchor_status = detail::anchor_status_t::NONE; + m_attrs &= ~detail::node_attr_mask::anchoring; auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); mp_meta.reset(); itr->second.swap(*this); mp_meta->anchor_table.erase(itr); @@ -9681,9 +12076,11 @@ class basic_node { node.swap(*this); p_meta->anchor_table.emplace(anchor_name, std::move(node)); + m_attrs &= ~detail::node_attr_mask::anchoring; + m_attrs |= detail::node_attr_bits::anchor_bit; mp_meta = p_meta; - m_prop.anchor_status = detail::anchor_status_t::ANCHOR; - m_prop.anchor_offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + uint32_t offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + detail::node_attr_bits::set_anchor_offset(offset, m_attrs); m_prop.anchor = anchor_name; } @@ -9693,9 +12090,9 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/ void add_anchor_name(std::string&& anchor_name) { if (is_anchor()) { - m_prop.anchor_status = detail::anchor_status_t::NONE; + m_attrs &= ~detail::node_attr_mask::anchoring; auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); mp_meta.reset(); itr->second.swap(*this); mp_meta->anchor_table.erase(itr); @@ -9707,9 +12104,11 @@ class basic_node { node.swap(*this); p_meta->anchor_table.emplace(anchor_name, std::move(node)); + m_attrs &= ~detail::node_attr_mask::anchoring; + m_attrs |= detail::node_attr_bits::anchor_bit; mp_meta = p_meta; - m_prop.anchor_status = detail::anchor_status_t::ANCHOR; - m_prop.anchor_offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + uint32_t offset = static_cast(mp_meta->anchor_table.count(anchor_name) - 1); + detail::node_attr_bits::set_anchor_offset(offset, m_attrs); m_prop.anchor = std::move(anchor_name); } @@ -9726,7 +12125,7 @@ class basic_node { /// @return The tag name associated with the node. It may be empty. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_tag_name/ const std::string& get_tag_name() const { - if (!has_tag_name()) { + if FK_YAML_UNLIKELY (!has_tag_name()) { throw fkyaml::exception("No tag name has been set."); } return m_prop.tag; @@ -9765,7 +12164,7 @@ class basic_node { auto ret = ValueType(); if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); ConverterType::from_node(itr->second, ret); } else { @@ -9782,7 +12181,7 @@ class basic_node { ReferenceType get_value_ref() { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return itr->second.get_value_ref_impl(static_cast>(nullptr)); } return get_value_ref_impl(static_cast>(nullptr)); @@ -9801,7 +12200,7 @@ class basic_node { ReferenceType get_value_ref() const { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return itr->second.get_value_ref_impl(static_cast>(nullptr)); } return get_value_ref_impl(static_cast>(nullptr)); @@ -9812,7 +12211,7 @@ class basic_node { /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/swap/ void swap(basic_node& rhs) noexcept { using std::swap; - swap(m_node_type, rhs.m_node_type); + swap(m_attrs, rhs.m_attrs); swap(mp_meta, rhs.mp_meta); node_value tmp {}; @@ -9821,9 +12220,7 @@ class basic_node { std::memcpy(&rhs.m_node_value, &tmp, sizeof(node_value)); swap(m_prop.tag, rhs.m_prop.tag); - swap(m_prop.anchor_status, rhs.m_prop.anchor_status); swap(m_prop.anchor, rhs.m_prop.anchor); - swap(m_prop.anchor_offset, rhs.m_prop.anchor_offset); } /// @brief Returns the first iterator of basic_node values of container types (sequence or mapping) from a non-const @@ -9831,19 +12228,19 @@ class basic_node { /// @return An iterator to the first element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/ iterator begin() { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->begin()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->begin()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -9852,19 +12249,19 @@ class basic_node { /// @return A constant iterator to the first element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/ const_iterator begin() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->begin()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->begin()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -9873,19 +12270,19 @@ class basic_node { /// @return An iterator to the past-the end element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/ iterator end() { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->end()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->end()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -9894,19 +12291,19 @@ class basic_node { /// @return A constant iterator to the past-the end element of a YAML node value (either sequence or mapping). /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/ const_iterator end() const { - switch (type()) { - case node_t::SEQUENCE: { + switch (get_node_attrs() & detail::node_attr_mask::value) { + case detail::node_attr_bits::seq_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_sequence != nullptr); return {detail::sequence_iterator_tag(), p_node_value->p_sequence->end()}; } - case node_t::MAPPING: { + case detail::node_attr_bits::map_bit: { const node_value* p_node_value = get_node_value_ptr(); FK_YAML_ASSERT(p_node_value->p_mapping != nullptr); return {detail::mapping_iterator_tag(), p_node_value->p_mapping->end()}; } default: - throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", type()); + throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type()); } } @@ -9916,134 +12313,143 @@ class basic_node { const node_value* get_node_value_ptr() const { if (has_anchor_name()) { auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; - std::advance(itr, m_prop.anchor_offset); + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); return &(itr->second.m_node_value); } return &m_node_value; } + detail::node_attr_t get_node_attrs() const { + if (has_anchor_name()) { + auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first; + std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs)); + return itr->second.m_attrs; + } + return m_attrs; + } + /// @brief Returns reference to the sequence node value. /// @throw fkyaml::exception The node value is not a sequence. /// @return Reference to the sequence node value. sequence_type& get_value_ref_impl(sequence_type* /*unused*/) { - if (!is_sequence()) { - throw fkyaml::type_error("The node value is not a sequence.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::seq_bit) { + return *(m_node_value.p_sequence); } - return *(m_node_value.p_sequence); + throw fkyaml::type_error("The node value is not a sequence.", get_type()); } /// @brief Returns constant reference to the sequence node value. /// @throw fkyaml::exception The node value is not a sequence. /// @return Constant reference to the sequence node value. const sequence_type& get_value_ref_impl(const sequence_type* /*unused*/) const { - if (!is_sequence()) { - throw fkyaml::type_error("The node value is not a sequence.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::seq_bit) { + return *(m_node_value.p_sequence); } - return *(m_node_value.p_sequence); + throw fkyaml::type_error("The node value is not a sequence.", get_type()); } /// @brief Returns reference to the mapping node value. /// @throw fkyaml::exception The node value is not a mapping. /// @return Reference to the mapping node value. mapping_type& get_value_ref_impl(mapping_type* /*unused*/) { - if (!is_mapping()) { - throw fkyaml::type_error("The node value is not a mapping.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::map_bit) { + return *(m_node_value.p_mapping); } - return *(m_node_value.p_mapping); + throw fkyaml::type_error("The node value is not a mapping.", get_type()); } /// @brief Returns constant reference to the mapping node value. /// @throw fkyaml::exception The node value is not a mapping. /// @return Constant reference to the mapping node value. const mapping_type& get_value_ref_impl(const mapping_type* /*unused*/) const { - if (!is_mapping()) { - throw fkyaml::type_error("The node value is not a mapping.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::map_bit) { + return *(m_node_value.p_mapping); } - return *(m_node_value.p_mapping); + throw fkyaml::type_error("The node value is not a mapping.", get_type()); } /// @brief Returns reference to the boolean node value. /// @throw fkyaml::exception The node value is not a boolean. /// @return Reference to the boolean node value. boolean_type& get_value_ref_impl(boolean_type* /*unused*/) { - if (!is_boolean()) { - throw fkyaml::type_error("The node value is not a boolean.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::bool_bit) { + return m_node_value.boolean; } - return m_node_value.boolean; + throw fkyaml::type_error("The node value is not a boolean.", get_type()); } /// @brief Returns reference to the boolean node value. /// @throw fkyaml::exception The node value is not a boolean. /// @return Constant reference to the boolean node value. const boolean_type& get_value_ref_impl(const boolean_type* /*unused*/) const { - if (!is_boolean()) { - throw fkyaml::type_error("The node value is not a boolean.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::bool_bit) { + return m_node_value.boolean; } - return m_node_value.boolean; + throw fkyaml::type_error("The node value is not a boolean.", get_type()); } /// @brief Returns reference to the integer node value. /// @throw fkyaml::exception The node value is not an integer. /// @return Reference to the integer node value. integer_type& get_value_ref_impl(integer_type* /*unused*/) { - if (!is_integer()) { - throw fkyaml::type_error("The node value is not an integer.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::int_bit) { + return m_node_value.integer; } - return m_node_value.integer; + throw fkyaml::type_error("The node value is not an integer.", get_type()); } /// @brief Returns reference to the integer node value. /// @throw fkyaml::exception The node value is not an integer. /// @return Constant reference to the integer node value. const integer_type& get_value_ref_impl(const integer_type* /*unused*/) const { - if (!is_integer()) { - throw fkyaml::type_error("The node value is not an integer.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::int_bit) { + return m_node_value.integer; } - return m_node_value.integer; + throw fkyaml::type_error("The node value is not an integer.", get_type()); } /// @brief Returns reference to the floating point number node value. /// @throw fkyaml::exception The node value is not a floating point number. /// @return Reference to the floating point number node value. float_number_type& get_value_ref_impl(float_number_type* /*unused*/) { - if (!is_float_number()) { - throw fkyaml::type_error("The node value is not a floating point number.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::float_bit) { + return m_node_value.float_val; } - return m_node_value.float_val; + throw fkyaml::type_error("The node value is not a floating point number.", get_type()); } /// @brief Returns reference to the floating point number node value. /// @throw fkyaml::exception The node value is not a floating point number. /// @return Constant reference to the floating point number node value. const float_number_type& get_value_ref_impl(const float_number_type* /*unused*/) const { - if (!is_float_number()) { - throw fkyaml::type_error("The node value is not a floating point number.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::float_bit) { + return m_node_value.float_val; } - return m_node_value.float_val; + throw fkyaml::type_error("The node value is not a floating point number.", get_type()); } /// @brief Returns reference to the string node value. /// @throw fkyaml::exception The node value is not a string. /// @return Reference to the string node value. string_type& get_value_ref_impl(string_type* /*unused*/) { - if (!is_string()) { - throw fkyaml::type_error("The node value is not a string.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::string_bit) { + return *(m_node_value.p_string); } - return *(m_node_value.p_string); + throw fkyaml::type_error("The node value is not a string.", get_type()); } /// @brief Returns reference to the string node value. /// @throw fkyaml::exception The node value is not a string. /// @return Constant reference to the string node value. const string_type& get_value_ref_impl(const string_type* /*unused*/) const { - if (!is_string()) { - throw fkyaml::type_error("The node value is not a string.", type()); + if FK_YAML_LIKELY (m_attrs & detail::node_attr_bits::string_bit) { + return *(m_node_value.p_string); } - return *(m_node_value.p_string); + throw fkyaml::type_error("The node value is not a string.", get_type()); } - /// The current node value type. - node_t m_node_type {node_t::NULL_OBJECT}; + /// The current node attributes. + detail::node_attr_t m_attrs {detail::node_attr_bits::default_bits}; /// The shared set of YAML directives applied to this node. mutable std::shared_ptr> mp_meta { std::shared_ptr>(new detail::document_metainfo())}; @@ -10140,7 +12546,7 @@ inline fkyaml::node operator"" _yaml(const char32_t* s, std::size_t n) { return fkyaml::node::deserialize(std::move(s), std::move(s + n)); } -#ifdef FK_YAML_HAS_CHAR8_T +#if FK_YAML_HAS_CHAR8_T /// @brief The user-defined string literal which deserializes a `char8_t` array into a `node` object. /// @param s An input `char8_t` array. /// @param n The size of `s`. diff --git a/test/cmake_add_subdirectory_test/project/main.cpp b/test/cmake_add_subdirectory_test/project/main.cpp index 46f80ee9..137bcac8 100644 --- a/test/cmake_add_subdirectory_test/project/main.cpp +++ b/test/cmake_add_subdirectory_test/project/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/cmake_fetch_content_test/project/CMakeLists.txt b/test/cmake_fetch_content_test/project/CMakeLists.txt index 7f82e1e1..ab338859 100644 --- a/test/cmake_fetch_content_test/project/CMakeLists.txt +++ b/test/cmake_fetch_content_test/project/CMakeLists.txt @@ -6,7 +6,7 @@ include(FetchContent) FetchContent_Declare( fkYAML GIT_REPOSITORY https://github.com/fktn-k/fkYAML.git - GIT_TAG v0.3.11) + GIT_TAG v0.3.12) FetchContent_MakeAvailable(fkYAML) add_executable( diff --git a/test/cmake_fetch_content_test/project/main.cpp b/test/cmake_fetch_content_test/project/main.cpp index 46f80ee9..137bcac8 100644 --- a/test/cmake_fetch_content_test/project/main.cpp +++ b/test/cmake_fetch_content_test/project/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/cmake_find_package_test/project/main.cpp b/test/cmake_find_package_test/project/main.cpp index 46f80ee9..137bcac8 100644 --- a/test/cmake_find_package_test/project/main.cpp +++ b/test/cmake_find_package_test/project/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/cmake_target_include_directories_test/project/main.cpp b/test/cmake_target_include_directories_test/project/main.cpp index 46f80ee9..137bcac8 100644 --- a/test/cmake_target_include_directories_test/project/main.cpp +++ b/test/cmake_target_include_directories_test/project/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/CMakeLists.txt b/test/unit_test/CMakeLists.txt index 8d122890..40162f3c 100644 --- a/test/unit_test/CMakeLists.txt +++ b/test/unit_test/CMakeLists.txt @@ -110,18 +110,25 @@ target_compile_options( $<$: /W4 /WX /EHsc /utf-8 /permissive- /wd4709 # comma operator within array index expression + /wd4996 # for testing deprecated functions $<$:/Z7> $<$:/Od> > # GNU $<$: -Wall -Wextra -Werror -pedantic -Wpedantic --all-warnings --extra-warnings + -Wno-deprecated-declarations # for testing deprecated functions -Wno-self-move # necessary to build the detail::iterator class test > # Clang $<$: -Wall -Wextra -Werror -pedantic -Wno-c++98-compat -Wno-c++98-compat-pedantic + -Wno-deprecated-declarations # for testing deprecated functions + > + $<$: + -Wall -Wextra -Werror -pedantic + -Wno-deprecated-declarations # for testing deprecated functions > # IntelLLVM $<$: @@ -153,7 +160,7 @@ if(FK_YAML_RUN_CLANG_SANITIZERS) -fno-omit-frame-pointer -fsanitize=address,undefined,bounds,integer,nullability -fno-sanitize-recover=all - -fno-sanitize=unsigned-integer-overflow,unsigned-shift-base + -fno-sanitize=signed-integer-overflow,implicit-conversion ) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) @@ -163,7 +170,7 @@ if(FK_YAML_RUN_CLANG_SANITIZERS) -fno-omit-frame-pointer -fsanitize=address,undefined,bounds,integer,nullability -fno-sanitize-recover=all - -fno-sanitize=unsigned-integer-overflow,unsigned-shift-base + -fno-sanitize=signed-integer-overflow,implicit-conversion ) else() target_link_libraries( @@ -172,7 +179,7 @@ if(FK_YAML_RUN_CLANG_SANITIZERS) -fno-omit-frame-pointer -fsanitize=address,undefined,bounds,integer,nullability -fno-sanitize-recover=all - -fno-sanitize=unsigned-integer-overflow,unsigned-shift-base + -fno-sanitize=signed-integer-overflow,implicit-conversion ) endif() endif() @@ -212,23 +219,27 @@ add_executable( ${TEST_TARGET} test_custom_from_node.cpp test_deserializer_class.cpp - test_encode_detector.cpp test_exception_class.cpp - test_from_string.cpp test_input_adapter.cpp test_iterator_class.cpp test_lexical_analyzer_class.cpp + test_node_attrs.cpp test_node_class.cpp test_node_ref_storage_class.cpp + test_node_type.cpp test_ordered_map_class.cpp test_position_tracker_class.cpp + test_scalar_conv.cpp test_scalar_scanner_class.cpp test_serializer_class.cpp + test_str_view_class.cpp test_string_formatter.cpp test_tag_resolver_class.cpp test_uri_encoding_class.cpp + test_utf_encode_detector.cpp test_utf_encodings.cpp test_yaml_escaper_class.cpp + test_yaml_version_type.cpp main.cpp ) diff --git a/test/unit_test/main.cpp b/test/unit_test/main.cpp index 794ef36e..4a3f79ab 100644 --- a/test/unit_test/main.cpp +++ b/test/unit_test/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_custom_from_node.cpp b/test/unit_test/test_custom_from_node.cpp index d794eaf2..9af98dc8 100644 --- a/test/unit_test/test_custom_from_node.cpp +++ b/test/unit_test/test_custom_from_node.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_deserializer_class.cpp b/test/unit_test/test_deserializer_class.cpp index d42ad524..68193861 100644 --- a/test/unit_test/test_deserializer_class.cpp +++ b/test/unit_test/test_deserializer_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -152,6 +152,18 @@ TEST_CASE("Deserializer_FloatingPointNumberKey") { } } +TEST_CASE("Deserializer_ScalarConversionErrorHandling") { + fkyaml::detail::basic_deserializer deserializer; + fkyaml::node root; + + auto input = GENERATE( + std::string("!!null foo: bar"), + std::string("!!bool foo: bar"), + std::string("!!int foo: bar"), + std::string("!!float foo: bar")); + REQUIRE_THROWS_AS(root = deserializer.deserialize(fkyaml::detail::input_adapter(input)), fkyaml::parse_error); +} + TEST_CASE("Deserializer_InvalidIndentation") { fkyaml::detail::basic_deserializer deserializer; fkyaml::node root; @@ -1905,13 +1917,13 @@ TEST_CASE("Deserializer_YAMLVerDirective") { SECTION("YAML 1.1") { REQUIRE_NOTHROW(root = deserializer.deserialize(fkyaml::detail::input_adapter("%YAML 1.1\n---\nfoo: one"))); - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_1); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1); REQUIRE(root.is_mapping()); REQUIRE(root.size() == 1); REQUIRE(root.contains("foo")); fkyaml::node& foo_node = root["foo"]; - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_1); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1); REQUIRE(foo_node.is_string()); REQUIRE(foo_node.get_value_ref() == "one"); } @@ -1919,13 +1931,13 @@ TEST_CASE("Deserializer_YAMLVerDirective") { SECTION("YAML 1.2") { REQUIRE_NOTHROW(root = deserializer.deserialize(fkyaml::detail::input_adapter("%YAML 1.2\n---\nfoo: one"))); - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); REQUIRE(root.is_mapping()); REQUIRE(root.size() == 1); REQUIRE(root.contains("foo")); fkyaml::node& foo_node = root["foo"]; - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); REQUIRE(foo_node.is_string()); REQUIRE(foo_node.get_value_ref() == "one"); } @@ -1934,7 +1946,7 @@ TEST_CASE("Deserializer_YAMLVerDirective") { REQUIRE_NOTHROW( root = deserializer.deserialize(fkyaml::detail::input_adapter("foo: bar\n%YAML 1.1\ntrue: 123"))); - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); REQUIRE(root.is_mapping()); REQUIRE(root.size() == 2); REQUIRE(root.contains("foo")); @@ -2867,7 +2879,7 @@ TEST_CASE("Deserializer_DocumentWithMarkers") { REQUIRE_NOTHROW(root = deserializer.deserialize(fkyaml::detail::input_adapter(input))); REQUIRE(root.is_mapping()); REQUIRE(root.size() == 1); - REQUIRE(root.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); + REQUIRE(root.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); REQUIRE(root.contains("foo")); fkyaml::node& foo_node = root["foo"]; diff --git a/test/unit_test/test_exception_class.cpp b/test/unit_test/test_exception_class.cpp index adc8b257..9fadf672 100644 --- a/test/unit_test/test_exception_class.cpp +++ b/test/unit_test/test_exception_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_from_string.cpp b/test/unit_test/test_from_string.cpp deleted file mode 100644 index 8edffc0c..00000000 --- a/test/unit_test/test_from_string.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// _______ __ __ __ _____ __ __ __ -// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 -// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML -// -// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani -// SPDX-License-Identifier: MIT - -#include -#include -#include - -#include - -#include - -TEST_CASE("FromString_Null") { - SECTION("valid string for the null value") { - auto input = GENERATE(std::string("null"), std::string("Null"), std::string("NULL"), std::string("~")); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == nullptr); - } - - SECTION("invalid string for the null value") { - std::string input("test"); - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } -} - -TEST_CASE("FromString_Bool") { - SECTION("valid string for the true value") { - auto input = GENERATE(std::string("true"), std::string("True"), std::string("TRUE")); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == true); - } - - SECTION("valid string for the false value") { - auto input = GENERATE(std::string("false"), std::string("False"), std::string("FALSE")); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == false); - } - - SECTION("invalid string for the boolean values") { - std::string input("test"); - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } -} - -TEST_CASE("FromString_Integer") { - SECTION("char type") { - std::string input("-64"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == -64); - - input = "256"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("unsigned char type") { - std::string input("64"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == 64); - - input = "512"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("short type") { - std::string input("-15464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == -15464); - - input = "45464"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("unsigned short type") { - std::string input("15464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == 15464); - - input = "-1"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("int type") { - std::string input("-1154357464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == -1154357464); - - input = "3154357464"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("unsigned int type") { - std::string input("3154357464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == 3154357464u); - - input = "999999999999999999999999"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("long type") { - std::string input("-1154357464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == -1154357464l); - - input = "9413456789012123456"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("unsigned long type") { - std::string input("317464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == 317464ul); - - input = "999999999999999999999999"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("long long type") { - std::string input("-1154357464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == -1154357464ll); - - input = "18413456789012123456"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } - - SECTION("unsigned long long type") { - std::string input("3154357464"); - REQUIRE(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == 3154357464ull); - - input = "999999999999999999999999"; - REQUIRE_THROWS_AS( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } -} - -TEST_CASE("FromString_Float") { - SECTION("positive infinity") { - auto input = GENERATE(std::string(".inf"), std::string(".Inf"), std::string(".INF")); - REQUIRE( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == - std::numeric_limits::infinity()); - } - - SECTION("negative infinity") { - auto input = GENERATE(std::string("-.inf"), std::string("-.Inf"), std::string("-.INF")); - REQUIRE( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == - -1 * std::numeric_limits::infinity()); - } - - SECTION("NaN") { - auto input = GENERATE(std::string(".nan"), std::string(".NaN"), std::string(".NAN")); - float ret = 0.0f; - REQUIRE_NOTHROW(ret = fkyaml::detail::from_string(input, fkyaml::detail::type_tag {})); - REQUIRE(std::isnan(ret)); - } - - SECTION("valid string for a float value") { - std::string input("3.14"); - REQUIRE(std::abs(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) - 3.14f) < FLT_EPSILON); - - input = "3.40282347e+39"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } -} - -TEST_CASE("FromString_Double") { - SECTION("positive infinity") { - auto input = GENERATE(std::string(".inf"), std::string(".Inf"), std::string(".INF")); - REQUIRE( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == - std::numeric_limits::infinity()); - } - - SECTION("negative infinity") { - auto input = GENERATE(std::string("-.inf"), std::string("-.Inf"), std::string("-.INF")); - REQUIRE( - fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) == - -1 * std::numeric_limits::infinity()); - } - - SECTION("NaN") { - auto input = GENERATE(std::string(".nan"), std::string(".NaN"), std::string(".NAN")); - double ret = 0.0; - REQUIRE_NOTHROW(ret = fkyaml::detail::from_string(input, fkyaml::detail::type_tag {})); - REQUIRE(std::isnan(ret)); - } - - SECTION("valid string for a double value") { - std::string input("3.14"); - REQUIRE(std::abs(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}) - 3.14) < DBL_EPSILON); - - input = "1.7976931348623157E+309"; - REQUIRE_THROWS_AS(fkyaml::detail::from_string(input, fkyaml::detail::type_tag {}), fkyaml::exception); - } -} diff --git a/test/unit_test/test_input_adapter.cpp b/test/unit_test/test_input_adapter.cpp index bf27388a..21b407e2 100644 --- a/test/unit_test/test_input_adapter.cpp +++ b/test/unit_test/test_input_adapter.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -172,8 +172,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8N") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 12); REQUIRE(buffer[0] == 't'); @@ -196,8 +195,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8N") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 12); REQUIRE(buffer[0] == 't'); @@ -222,8 +220,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8N") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -243,8 +240,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8N") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -265,8 +261,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8BOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 12); REQUIRE(buffer[0] == 't'); @@ -291,8 +286,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8BOM") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 12); REQUIRE(buffer[0] == 't'); @@ -317,8 +311,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8BOM") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -338,8 +331,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8BOM") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -359,8 +351,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -380,8 +371,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 'a'); @@ -401,8 +391,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -423,8 +412,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { using itr_type = typename std::u16string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 'a'); @@ -447,8 +435,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -469,8 +456,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEN") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -492,8 +478,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -514,8 +499,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -534,8 +518,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -556,8 +539,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { using itr_type = typename std::u16string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -579,8 +561,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -601,8 +582,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer[0] == 'a'); REQUIRE(buffer[1] == char(0xE3u)); @@ -622,8 +602,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -643,8 +622,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -663,8 +641,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -685,8 +662,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { using itr_type = typename std::u16string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -708,8 +684,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -730,8 +705,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEN") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -753,8 +727,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -775,8 +748,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -795,8 +767,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -817,8 +788,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { using itr_type = typename std::u16string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -840,8 +810,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -862,8 +831,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 9); REQUIRE(buffer[0] == 'a'); @@ -884,8 +852,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -904,8 +871,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -923,8 +889,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -944,8 +909,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { using itr_type = typename std::u32string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -966,8 +930,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -987,8 +950,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEN") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1008,8 +970,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1028,8 +989,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1047,8 +1007,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1068,8 +1027,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { using itr_type = typename std::u32string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1090,8 +1048,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1111,8 +1068,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BEBOM") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1132,8 +1088,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1152,8 +1107,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1171,8 +1125,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1192,8 +1145,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { using itr_type = typename std::u32string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1214,8 +1166,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1235,8 +1186,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEN") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1256,8 +1206,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1276,8 +1225,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { using itr_type = typename std::string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1295,8 +1243,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1316,8 +1263,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { using itr_type = typename std::u32string::iterator; REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1338,8 +1284,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1359,8 +1304,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32LEBOM") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == 'a'); @@ -1379,6 +1323,27 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { // UTF-8 1-Byte Characters // ///////////////////////////////// + SECTION("iterator_input_adapter with valid 1-byte UTF-8 encodings") { + char input[] = {0x5A, 0x30, 0x61, 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); + + REQUIRE(buffer.size() == 3); + REQUIRE(buffer[0] == char(0x5Au)); + REQUIRE(buffer[1] == char(0x30u)); + REQUIRE(buffer[2] == char(0x61u)); + } + + SECTION("iterator_input_adapter with invalid 1-byte UTF-8 encodings") { + char input[] = {char(0x81u), char(0x82u), char(0x83u), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); + } + SECTION("file_input_adapter with valid 1-byte UTF-8 encodings") { DISABLE_C4996 FILE* p_file = std::fopen(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8n_valid_1byte_char.txt", "r"); @@ -1387,8 +1352,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 3); REQUIRE(buffer[0] == char(0x5Au)); @@ -1406,8 +1370,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); std::fclose(p_file); } @@ -1417,8 +1380,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 3); REQUIRE(buffer[0] == char(0x5Au)); @@ -1431,14 +1393,35 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); } ///////////////////////////////// // UTF-8 2-Byte Characters // ///////////////////////////////// + SECTION("iterator_input_adapter with valid 2-byte UTF-8 encodings") { + char input[] = {char(0xC2u), char(0x80u), char(0xDFu), char(0xBFu), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); + + REQUIRE(buffer.size() == 4); + REQUIRE(buffer[0] == char(0xC2u)); + REQUIRE(buffer[1] == char(0x80u)); + REQUIRE(buffer[2] == char(0xDFu)); + REQUIRE(buffer[3] == char(0xBFu)); + } + + SECTION("iterator_input_adapter with invalid 2-byte UTF-8 encodings") { + char input[] = {char(0xC1u), char(0x80u), char(0xC2u), 0x7F, 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); + } + SECTION("file_input_adapter with valid 2-byte UTF-8 encodings") { DISABLE_C4996 FILE* p_file = std::fopen(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8n_valid_2byte_char.txt", "r"); @@ -1447,8 +1430,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 4); REQUIRE(buffer[0] == char(0xC2u)); @@ -1467,8 +1449,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); std::fclose(p_file); } @@ -1478,8 +1459,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 4); REQUIRE(buffer[0] == char(0xC2u)); @@ -1493,14 +1473,37 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); } ///////////////////////////////// // UTF-8 3-Byte Characters // ///////////////////////////////// + SECTION("iterator_input_adapter with valid 3-byte UTF-8 encodings") { + char input[] = {char(0xE0u), char(0x80u), char(0x80u), char(0xECu), char(0xBFu), char(0xBFu), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); + + REQUIRE(buffer.size() == 6); + REQUIRE(buffer[0] == char(0xE0u)); + REQUIRE(buffer[1] == char(0x80u)); + REQUIRE(buffer[2] == char(0x80u)); + REQUIRE(buffer[3] == char(0xECu)); + REQUIRE(buffer[4] == char(0xBFu)); + REQUIRE(buffer[5] == char(0xBFu)); + } + + SECTION("iterator_input_adapter with invalid 3-byte UTF-8 encodings") { + char input[] = {char(0xE0u), 0x6A, char(0x80u), char(0xEDu), char(0xA0u), char(0xC0u), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); + } + SECTION("file_input_adapter with valid 3-byte UTF-8 encodings") { DISABLE_C4996 FILE* p_file = std::fopen(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8n_valid_3byte_char.txt", "r"); @@ -1509,8 +1512,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 6); REQUIRE(buffer[0] == char(0xE0u)); @@ -1531,8 +1533,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); std::fclose(p_file); } @@ -1542,8 +1543,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 6); REQUIRE(buffer[0] == char(0xE0u)); @@ -1559,14 +1559,40 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); } ///////////////////////////////// // UTF-8 4-Byte Characters // ///////////////////////////////// + SECTION("iterator_input_adapter with valid 4-byte UTF-8 encodings") { + char input[] = { + char(0xF0u), char(0x90u), char(0x80u), char(0x80u), char(0xF2u), char(0xBFu), char(0x80u), char(0x80u), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); + + REQUIRE(buffer.size() == 8); + REQUIRE(buffer[0] == char(0xF0u)); + REQUIRE(buffer[1] == char(0x90u)); + REQUIRE(buffer[2] == char(0x80u)); + REQUIRE(buffer[3] == char(0x80u)); + REQUIRE(buffer[4] == char(0xF2u)); + REQUIRE(buffer[5] == char(0xBFu)); + REQUIRE(buffer[6] == char(0x80u)); + REQUIRE(buffer[7] == char(0x80u)); + } + + SECTION("iterator_input_adapter with invalid 4-byte UTF-8 encodings") { + char input[] = {char(0xF0u), char(0x80u), 0x70, 0x70, char(0xF4u), char(0xC0u), char(0xC0u), 0}; + auto input_adapter = fkyaml::detail::input_adapter(input); + REQUIRE(std::is_same>::value); + + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); + } + SECTION("file_input_adapter with valid 4-byte UTF-8 encodings") { DISABLE_C4996 FILE* p_file = std::fopen(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8n_valid_4byte_char.txt", "r"); @@ -1575,8 +1601,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == char(0xF0u)); @@ -1599,8 +1624,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); std::fclose(p_file); } @@ -1610,8 +1634,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 8); REQUIRE(buffer[0] == char(0xF0u)); @@ -1629,8 +1652,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8CharsValidation") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - REQUIRE_THROWS_AS(input_adapter.fill_buffer(buffer), fkyaml::invalid_encoding); + REQUIRE_THROWS_AS(input_adapter.get_buffer_view(), fkyaml::invalid_encoding); } } @@ -1640,8 +1662,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8NewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1662,8 +1683,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8NewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1687,8 +1707,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8NewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1710,8 +1729,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF8NewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1735,8 +1753,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1769,8 +1786,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1793,8 +1809,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1816,8 +1831,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF16BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1842,8 +1856,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1876,8 +1889,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(input); REQUIRE(std::is_same>::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1900,8 +1912,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(p_file); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); @@ -1923,8 +1934,7 @@ TEST_CASE("InputAdapter_FillBuffer_UTF32BENewlineCodeNormalization") { auto input_adapter = fkyaml::detail::input_adapter(ifs); REQUIRE(std::is_same::value); - std::string buffer {}; - input_adapter.fill_buffer(buffer); + fkyaml::detail::str_view buffer = input_adapter.get_buffer_view(); REQUIRE(buffer.size() == 10); REQUIRE(buffer[0] == 't'); diff --git a/test/unit_test/test_iterator_class.cpp b/test/unit_test/test_iterator_class.cpp index 0426b7f1..8c27fb5a 100644 --- a/test/unit_test/test_iterator_class.cpp +++ b/test/unit_test/test_iterator_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_lexical_analyzer_class.cpp b/test/unit_test/test_lexical_analyzer_class.cpp index 130689f9..3dbd39ef 100644 --- a/test/unit_test/test_lexical_analyzer_class.cpp +++ b/test/unit_test/test_lexical_analyzer_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -10,580 +10,413 @@ #include -using lexer_t = fkyaml::detail::lexical_analyzer; - TEST_CASE("LexicalAnalyzer_YamlVersionDirective") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("valid YAML directive") { - using value_pair_t = std::pair; + using value_pair_t = std::pair; auto value_pair = GENERATE( - value_pair_t(std::string("%YAML 1.1 "), std::string("1.1")), - value_pair_t(std::string("%YAML\t1.1\t"), std::string("1.1")), - value_pair_t(std::string("%YAML 1.1\n"), std::string("1.1")), - value_pair_t(std::string("%YAML 1.1"), std::string("1.1")), - value_pair_t(std::string("%YAML 1.2 "), std::string("1.2")), - value_pair_t(std::string("%YAML\t1.2\t"), std::string("1.2")), - value_pair_t(std::string("%YAML 1.2\n"), std::string("1.2")), - value_pair_t(std::string("%YAML 1.2"), std::string("1.2"))); + value_pair_t("%YAML 1.1 ", "1.1"), + value_pair_t("%YAML\t1.1\t", "1.1"), + value_pair_t("%YAML 1.1\n", "1.1"), + value_pair_t("%YAML 1.1", "1.1"), + value_pair_t("%YAML 1.2 ", "1.2"), + value_pair_t("%YAML\t1.2\t", "1.2"), + value_pair_t("%YAML 1.2\n", "1.2"), + value_pair_t("%YAML 1.2", "1.2")); - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); + fkyaml::detail::lexical_analyzer lexer(value_pair.first); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); REQUIRE(lexer.get_yaml_version() == value_pair.second); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("wrong YAML directive") { - auto buffer = GENERATE(std::string("%YUML 1.2"), std::string("%YANL 1.2 \n"), std::string("%YAML1.2")); + auto buffer = GENERATE( + fkyaml::detail::str_view("%YUML 1.2"), + fkyaml::detail::str_view("%YANL 1.2 \n"), + fkyaml::detail::str_view("%YAML1.2")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("invalid YAML directive value") { auto buffer = GENERATE( - std::string("%YAML 1.3\n"), - std::string("%YAML 2.0\n"), - std::string("%YAML 12"), - std::string("%YAML 123"), - std::string("%YAML 1.23"), - std::string("%YAML 1.11"), - std::string("%YAML 1.A"), - std::string("%YAML AbC")); - - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::str_view("%YAML 1.3\n"), + fkyaml::detail::str_view("%YAML 2.0\n"), + fkyaml::detail::str_view("%YAML 12"), + fkyaml::detail::str_view("%YAML 123"), + fkyaml::detail::str_view("%YAML 1.23"), + fkyaml::detail::str_view("%YAML 1.11"), + fkyaml::detail::str_view("%YAML 1.A"), + fkyaml::detail::str_view("%YAML AbC")); + + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_TagDirective") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("primary tag handle") { - auto input = GENERATE(std::string("%TAG ! foo"), std::string("%TAG\t!\tfoo")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + auto input = GENERATE(fkyaml::detail::str_view("%TAG ! foo"), fkyaml::detail::str_view("%TAG\t!\tfoo")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); - REQUIRE(lexer.get_tag_handle() == "!"); - REQUIRE(lexer.get_tag_prefix() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); + REQUIRE(lexer.get_tag_handle().compare("!") == 0); + REQUIRE(lexer.get_tag_prefix().compare("foo") == 0); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("secondary tag handle") { - auto input = GENERATE(std::string("%TAG !! foo"), std::string("%TAG\t!!\tfoo")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + auto input = GENERATE(fkyaml::detail::str_view("%TAG !! foo"), fkyaml::detail::str_view("%TAG\t!!\tfoo")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); - REQUIRE(lexer.get_tag_handle() == "!!"); - REQUIRE(lexer.get_tag_prefix() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); + REQUIRE(lexer.get_tag_handle().compare("!!") == 0); + REQUIRE(lexer.get_tag_prefix().compare("foo") == 0); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("named tag handle") { - auto input = GENERATE(std::string("%TAG !va1id-ta9! foo"), std::string("%TAG\t!va1id-ta9!\tfoo")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + auto input = GENERATE( + fkyaml::detail::str_view("%TAG !va1id-ta9! foo"), fkyaml::detail::str_view("%TAG\t!va1id-ta9!\tfoo")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); - REQUIRE(lexer.get_tag_handle() == "!va1id-ta9!"); - REQUIRE(lexer.get_tag_prefix() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::TAG_DIRECTIVE); + REQUIRE(lexer.get_tag_handle().compare("!va1id-ta9!") == 0); + REQUIRE(lexer.get_tag_prefix().compare("foo") == 0); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("invalid TAG directive") { - auto buffer = GENERATE(std::string("%TUB"), std::string("%TAC"), std::string("%TAGE")); + auto buffer = GENERATE( + fkyaml::detail::str_view("%TUB"), fkyaml::detail::str_view("%TAC"), fkyaml::detail::str_view("%TAGE")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("invalid tag handle") { auto input = GENERATE( - std::string("%TAG foo bar"), - std::string("%TAG !!abc bar"), - std::string("%TAG !"), - std::string("%TAG !!"), - std::string("%TAG !valid!"), - std::string("%TAG !invalid"), - std::string("%TAG !invalid bar"), - std::string("%TAG !invalid\tbar"), - std::string("%TAG !inv@lid! bar"), - std::string("%TAG !invalid!tag bar"), - std::string("%TAG !invalid")); - - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("%TAG foo bar"), + fkyaml::detail::str_view("%TAG !!abc bar"), + fkyaml::detail::str_view("%TAG !"), + fkyaml::detail::str_view("%TAG !!"), + fkyaml::detail::str_view("%TAG !valid!"), + fkyaml::detail::str_view("%TAG !invalid"), + fkyaml::detail::str_view("%TAG !invalid bar"), + fkyaml::detail::str_view("%TAG !invalid\tbar"), + fkyaml::detail::str_view("%TAG !inv@lid! bar"), + fkyaml::detail::str_view("%TAG !invalid!tag bar"), + fkyaml::detail::str_view("%TAG !invalid")); + + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } SECTION("invalid tag prefix") { auto input = GENERATE( - std::string("%TAG ! [invalid"), - std::string("%TAG !! ]invalid"), - std::string("%TAG !valid! {invalid"), - std::string("%TAG !valid! }invalid"), - std::string("%TAG !valid! ,invalid"), - std::string("%TAG !valid! %prefix")); - - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("%TAG ! [invalid"), + fkyaml::detail::str_view("%TAG !! ]invalid"), + fkyaml::detail::str_view("%TAG !valid! {invalid"), + fkyaml::detail::str_view("%TAG !valid! }invalid"), + fkyaml::detail::str_view("%TAG !valid! ,invalid"), + fkyaml::detail::str_view("%TAG !valid! %prefix")); + + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_InvalidDirective") { - auto buffer = GENERATE(std::string("%TAG"), std::string("%YAML")); + auto buffer = GENERATE(fkyaml::detail::str_view("%TAG"), fkyaml::detail::str_view("%YAML")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } TEST_CASE("LexicalAnalyzer_ReservedDirective") { - auto buffer = - GENERATE(std::string("%TEST\n"), std::string("%1984\n "), std::string("%TEST4LIB\n"), std::string("%%ERROR")); + auto buffer = GENERATE( + fkyaml::detail::str_view("%TEST\n"), + fkyaml::detail::str_view("%1984\n "), + fkyaml::detail::str_view("%TEST4LIB\n"), + fkyaml::detail::str_view("%%ERROR")); - fkyaml::detail::lexical_token_t token; - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_token token; + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_EmptyDirective") { - lexer_t lexer(fkyaml::detail::input_adapter("%")); - REQUIRE(lexer.get_next_token() == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); + fkyaml::detail::lexical_analyzer lexer("%"); + REQUIRE(lexer.get_next_token().type == fkyaml::detail::lexical_token_t::INVALID_DIRECTIVE); } TEST_CASE("LexicalAnalyzer_EndOfDirectives") { - lexer_t lexer(fkyaml::detail::input_adapter("%YAML 1.2\n---\nfoo: bar")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("%YAML 1.2\n---\nfoo: bar"); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); - REQUIRE(lexer.get_yaml_version() == "1.2"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); + REQUIRE(lexer.get_yaml_version() == fkyaml::detail::str_view("1.2")); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_DIRECTIVES); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_DIRECTIVES); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_EndOfDocuments") { - lexer_t lexer(fkyaml::detail::input_adapter("%YAML 1.2\n---\n...")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("%YAML 1.2\n---\n..."); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); - REQUIRE(lexer.get_yaml_version() == "1.2"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::YAML_VER_DIRECTIVE); + REQUIRE(lexer.get_yaml_version() == fkyaml::detail::str_view("1.2")); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_DIRECTIVES); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_DIRECTIVES); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_DOCUMENT); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_DOCUMENT); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_Colon") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("colon with half-width space") { - lexer_t lexer(fkyaml::detail::input_adapter(": ")); + fkyaml::detail::lexical_analyzer lexer(": "); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with LF newline code") { - lexer_t lexer(fkyaml::detail::input_adapter(":\n")); + fkyaml::detail::lexical_analyzer lexer(":\n"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with the end of the buffer") { - lexer_t lexer(fkyaml::detail::input_adapter(":")); + fkyaml::detail::lexical_analyzer lexer(":"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with a comment and a LF newline code") { - lexer_t lexer(fkyaml::detail::input_adapter(": # comment\n")); + fkyaml::detail::lexical_analyzer lexer(": # comment\n"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with a comment and no newline code") { - lexer_t lexer(fkyaml::detail::input_adapter(": # comment")); + fkyaml::detail::lexical_analyzer lexer(": # comment"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with many spaces and a LF newline code") { - lexer_t lexer(fkyaml::detail::input_adapter(": \n")); + fkyaml::detail::lexical_analyzer lexer(": \n"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with many spaces and no newline code") { - lexer_t lexer(fkyaml::detail::input_adapter(": ")); + fkyaml::detail::lexical_analyzer lexer(": "); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } SECTION("colon with an always-safe character") { - lexer_t lexer(fkyaml::detail::input_adapter(":test")); + fkyaml::detail::lexical_analyzer lexer(":test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == ":test"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == ":test"); } SECTION("colon with a flow indicator in a non-flow context") { - auto input = - GENERATE(std::string(":,"), std::string(":{"), std::string(":}"), std::string(":["), std::string(":]")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + auto input = GENERATE( + fkyaml::detail::str_view(":,"), + fkyaml::detail::str_view(":{"), + fkyaml::detail::str_view(":}"), + fkyaml::detail::str_view(":["), + fkyaml::detail::str_view(":]")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == input); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == input); } SECTION("colon with a flow indicator in a flow context") { auto input = GENERATE( - std::string("{:,"), std::string("{:{"), std::string("{:}"), std::string("{:["), std::string("{:]")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("{:,"), + fkyaml::detail::str_view("{:{"), + fkyaml::detail::str_view("{:}"), + fkyaml::detail::str_view("{:["), + fkyaml::detail::str_view("{:]")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); } } TEST_CASE("LexicalAnalzer_BlockSequenceEntryPrefix") { - auto input = GENERATE(std::string("- foo"), std::string("-\tfoo"), std::string("-\n foo")); + auto input = GENERATE( + fkyaml::detail::str_view("- foo"), fkyaml::detail::str_view("-\tfoo"), fkyaml::detail::str_view("-\n foo")); - fkyaml::detail::lexical_token_t token; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_token token; + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo"); -} - -TEST_CASE("LexicalAnalyzer_Null") { - fkyaml::detail::lexical_token_t token; - - SECTION("valid null scalar token") { - auto buffer = GENERATE(std::string("null"), std::string("Null"), std::string("NULL"), std::string("~")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::NULL_VALUE); - REQUIRE_NOTHROW(lexer.get_null()); - REQUIRE(lexer.get_null() == nullptr); - } - - SECTION("invalid token for null scalar") { - lexer_t lexer(fkyaml::detail::input_adapter("test")); - REQUIRE_NOTHROW(lexer.get_next_token()); - REQUIRE_THROWS_AS(lexer.get_null(), fkyaml::exception); - } -} - -TEST_CASE("LexicalAnalyzer_BooleanTrue") { - fkyaml::detail::lexical_token_t token; - - SECTION("valid boolean true scalar token") { - auto buffer = GENERATE(std::string("true"), std::string("True"), std::string("TRUE")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); - } - - SECTION("invalid token for boolean true scalar") { - lexer_t lexer(fkyaml::detail::input_adapter("test")); - REQUIRE_NOTHROW(lexer.get_next_token()); - REQUIRE_THROWS_AS(lexer.get_boolean(), fkyaml::exception); - } -} - -TEST_CASE("LexicalAnalyzer_BooleanFalse") { - fkyaml::detail::lexical_token_t token; - - SECTION("valid boolean false scalar token") { - auto buffer = GENERATE(std::string("false"), std::string("False"), std::string("FALSE")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == false); - } - - SECTION("invalid token for boolean true scalar") { - lexer_t lexer(fkyaml::detail::input_adapter("test")); - REQUIRE_NOTHROW(lexer.get_next_token()); - REQUIRE_THROWS_AS(lexer.get_boolean(), fkyaml::exception); - } -} - -TEST_CASE("LexicalAnalyzer_Integer") { - fkyaml::detail::lexical_token_t token; - - SECTION("valid integer scalar token") { - using value_pair_t = std::pair; - auto value_pair = GENERATE( - value_pair_t(std::string("-1234"), -1234), - value_pair_t(std::string("-853255"), -853255), - value_pair_t(std::string("-1"), -1), - value_pair_t(std::string("0"), 0), - value_pair_t(std::string("643"), 643), - value_pair_t(std::string("+123"), 123)); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INTEGER_VALUE); - REQUIRE_NOTHROW(lexer.get_integer()); - REQUIRE(lexer.get_integer() == value_pair.second); - } - - SECTION("invalid token for integer scalar") { - lexer_t lexer(fkyaml::detail::input_adapter("test")); - REQUIRE_NOTHROW(lexer.get_next_token()); - REQUIRE_THROWS_AS(lexer.get_integer(), fkyaml::exception); - } + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == fkyaml::detail::str_view("foo")); } -TEST_CASE("LexicalAnalyzer_OctalInteger") { - using value_pair_t = std::pair; +TEST_CASE("LexicalAnalyzer_PlainScalar") { + using value_pair_t = std::pair; auto value_pair = GENERATE( - value_pair_t(std::string("0o27"), 027), - value_pair_t(std::string("0o5"), 05), - value_pair_t(std::string("0o77772"), 077772), - value_pair_t(std::string("0o672"), 0672)); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; + value_pair_t("test", "test"), + value_pair_t("nop", "nop"), + value_pair_t("none", "none"), + value_pair_t("?test", "?test"), + value_pair_t(".NET", ".NET"), + value_pair_t(".on", ".on"), + value_pair_t(".n", ".n"), + value_pair_t("-t", "-t"), + value_pair_t("-foo", "-foo"), + value_pair_t("-.test", "-.test"), + value_pair_t("?", "?"), + value_pair_t("--foo", "--foo"), + value_pair_t("+123", "+123"), + value_pair_t("1.2.3", "1.2.3"), + value_pair_t("foo,bar", "foo,bar"), + value_pair_t("foo[bar", "foo[bar"), + value_pair_t("foo]bar", "foo]bar"), + value_pair_t("foo{bar", "foo{bar"), + value_pair_t("foo}bar", "foo}bar"), + value_pair_t("foo:bar", "foo:bar"), + value_pair_t("foo bar", "foo bar"), + value_pair_t("foo\"bar", "foo\"bar"), + value_pair_t("foo\'s bar", "foo\'s bar"), + value_pair_t("foo\\bar", "foo\\bar"), + value_pair_t("nullValue", "nullValue"), + value_pair_t("NullValue", "NullValue"), + value_pair_t("NULL_VALUE", "NULL_VALUE"), + value_pair_t("~Value", "~Value"), + value_pair_t("trueValue", "trueValue"), + value_pair_t("TrueValue", "TrueValue"), + value_pair_t("TRUE_VALUE", "TRUE_VALUE"), + value_pair_t("falseValue", "falseValue"), + value_pair_t("FalseValue", "FalseValue"), + value_pair_t("FALSE_VALUE", "FALSE_VALUE"), + value_pair_t(".infValue", ".infValue"), + value_pair_t(".InfValue", ".InfValue"), + value_pair_t(".INF_VALUE", ".INF_VALUE"), + value_pair_t("-.infValue", "-.infValue"), + value_pair_t("-.InfValue", "-.InfValue"), + value_pair_t("-.INF_VALUE", "-.INF_VALUE"), + value_pair_t(".nanValue", ".nanValue"), + value_pair_t(".NaNValue", ".NaNValue"), + value_pair_t(".NAN_VALUE", ".NAN_VALUE")); + + fkyaml::detail::lexical_analyzer lexer(value_pair.first); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INTEGER_VALUE); - REQUIRE_NOTHROW(lexer.get_integer()); - REQUIRE(lexer.get_integer() == value_pair.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == value_pair.second); } -TEST_CASE("LexicalAnalyzer_HexadecimalInteger") { - using value_pair_t = std::pair; +TEST_CASE("LexicalAnalyzer_SingleQuotedScalar") { + using value_pair_t = std::pair; auto value_pair = GENERATE( - value_pair_t(std::string("0xA04F"), 0xA04F), - value_pair_t(std::string("0xa7F3"), 0xa7F3), - value_pair_t(std::string("0xFf29Bc"), 0xFf29Bc)); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; + value_pair_t("\'\'", ""), + value_pair_t("\'foo\"bar\'", "foo\"bar"), + value_pair_t("\'foo bar\'", "foo bar"), + value_pair_t("\'foo\'\'bar\'", "foo\'bar"), + value_pair_t("\'foo\'\'bar\' ", "foo\'bar"), + value_pair_t("\'foo,bar\'", "foo,bar"), + value_pair_t("\'foo]bar\'", "foo]bar"), + value_pair_t("\'foo}bar\'", "foo}bar"), + value_pair_t("\'foo\"bar\'", "foo\"bar"), + value_pair_t("\'foo:bar\'", "foo:bar"), + value_pair_t("\'foo\\bar\'", "foo\\bar"), + + value_pair_t("\'foo\nbar\'", "foo bar"), + value_pair_t("\'foo \t\n \tbar\'", "foo bar"), + value_pair_t("\'foo\n\n \t\nbar\'", "foo\n\nbar"), + value_pair_t("\'\nfoo\n\n \t\nbar\'", " foo\n\nbar"), + value_pair_t("\'foo\nbar\n\'", "foo bar ")); + + fkyaml::detail::lexical_analyzer lexer(value_pair.first); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INTEGER_VALUE); - REQUIRE_NOTHROW(lexer.get_integer()); - REQUIRE(lexer.get_integer() == value_pair.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SINGLE_QUOTED_SCALAR); + REQUIRE(token.str == value_pair.second); } -TEST_CASE("LexicalAnalyzer_FloatingPointNumber") { - fkyaml::detail::lexical_token_t token; - - SECTION("valid floating point number scalar token") { - using value_pair_t = std::pair; - auto value_pair = GENERATE( - value_pair_t(std::string("-1.234"), -1.234), - value_pair_t(std::string("-21."), -21.), - value_pair_t(std::string("0."), 0.), - value_pair_t(std::string("12."), 12.), - value_pair_t(std::string("567.8"), 567.8), - value_pair_t(std::string("0.24"), 0.24), - value_pair_t(std::string("9.8e-3"), 9.8e-3), - value_pair_t(std::string("3.95E3"), 3.95e3), - value_pair_t(std::string("1.863e+3"), 1.863e+3)); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == value_pair.second); - } - - SECTION("valid floating point number scalar token edge cases") { - auto input = GENERATE(std::string("1.23e"), std::string("1.2e-z")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); - REQUIRE_FALSE(lexer.get_next_token() == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - } - - SECTION("invalid token for floating point number scalar") { - lexer_t lexer(fkyaml::detail::input_adapter("test")); - REQUIRE_NOTHROW(lexer.get_next_token()); - REQUIRE_THROWS_AS(lexer.get_float_number(), fkyaml::exception); - } -} - -TEST_CASE("LexicalAnalyzer_Infinity") { - auto buffer = GENERATE( - std::string(".inf"), - std::string(".Inf"), - std::string(".INF"), - std::string("-.inf"), - std::string("-.Inf"), - std::string("-.INF")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); - - REQUIRE(lexer.get_next_token() == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(std::isinf(lexer.get_float_number()) == true); -} - -TEST_CASE("LexicalAnalyzer_NaN") { - auto buffer = GENERATE(std::string(".nan"), std::string(".NaN"), std::string(".NAN")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); - - REQUIRE(lexer.get_next_token() == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(std::isnan(lexer.get_float_number()) == true); -} - -TEST_CASE("LexicalAnalyzer_PlainString") { - using value_pair_t = std::pair; +TEST_CASE("LexicalAnalyzer_DoubleQuotedScalar") { + using value_pair_t = std::pair; auto value_pair = GENERATE( - value_pair_t(std::string("test"), fkyaml::node::string_type("test")), - value_pair_t(std::string("nop"), fkyaml::node::string_type("nop")), - value_pair_t(std::string("none"), fkyaml::node::string_type("none")), - value_pair_t(std::string("?test"), fkyaml::node::string_type("?test")), - value_pair_t(std::string(".NET"), fkyaml::node::string_type(".NET")), - value_pair_t(std::string(".on"), fkyaml::node::string_type(".on")), - value_pair_t(std::string(".n"), fkyaml::node::string_type(".n")), - value_pair_t(std::string("-t"), fkyaml::node::string_type("-t")), - value_pair_t(std::string("-foo"), fkyaml::node::string_type("-foo")), - value_pair_t(std::string("-.test"), fkyaml::node::string_type("-.test")), - value_pair_t(std::string("?"), fkyaml::node::string_type("?")), - value_pair_t(std::string("1.2.3"), fkyaml::node::string_type("1.2.3")), - value_pair_t(std::string("foo,bar"), fkyaml::node::string_type("foo,bar")), - value_pair_t(std::string("foo[bar"), fkyaml::node::string_type("foo[bar")), - value_pair_t(std::string("foo]bar"), fkyaml::node::string_type("foo]bar")), - value_pair_t(std::string("foo{bar"), fkyaml::node::string_type("foo{bar")), - value_pair_t(std::string("foo}bar"), fkyaml::node::string_type("foo}bar")), - value_pair_t(std::string("foo:bar"), fkyaml::node::string_type("foo:bar")), - value_pair_t(std::string("foo bar"), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("foo\"bar"), fkyaml::node::string_type("foo\"bar")), - value_pair_t(std::string("foo\'s bar"), fkyaml::node::string_type("foo\'s bar")), - value_pair_t(std::string("foo\\bar"), fkyaml::node::string_type("foo\\bar")), - value_pair_t(std::string("nullValue"), fkyaml::node::string_type("nullValue")), - value_pair_t(std::string("NullValue"), fkyaml::node::string_type("NullValue")), - value_pair_t(std::string("NULL_VALUE"), fkyaml::node::string_type("NULL_VALUE")), - value_pair_t(std::string("~Value"), fkyaml::node::string_type("~Value")), - value_pair_t(std::string("trueValue"), fkyaml::node::string_type("trueValue")), - value_pair_t(std::string("TrueValue"), fkyaml::node::string_type("TrueValue")), - value_pair_t(std::string("TRUE_VALUE"), fkyaml::node::string_type("TRUE_VALUE")), - value_pair_t(std::string("falseValue"), fkyaml::node::string_type("falseValue")), - value_pair_t(std::string("FalseValue"), fkyaml::node::string_type("FalseValue")), - value_pair_t(std::string("FALSE_VALUE"), fkyaml::node::string_type("FALSE_VALUE")), - value_pair_t(std::string(".infValue"), fkyaml::node::string_type(".infValue")), - value_pair_t(std::string(".InfValue"), fkyaml::node::string_type(".InfValue")), - value_pair_t(std::string(".INF_VALUE"), fkyaml::node::string_type(".INF_VALUE")), - value_pair_t(std::string("-.infValue"), fkyaml::node::string_type("-.infValue")), - value_pair_t(std::string("-.InfValue"), fkyaml::node::string_type("-.InfValue")), - value_pair_t(std::string("-.INF_VALUE"), fkyaml::node::string_type("-.INF_VALUE")), - value_pair_t(std::string(".nanValue"), fkyaml::node::string_type(".nanValue")), - value_pair_t(std::string(".NaNValue"), fkyaml::node::string_type(".NaNValue")), - value_pair_t(std::string(".NAN_VALUE"), fkyaml::node::string_type(".NAN_VALUE"))); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; + value_pair_t("\"\"", ""), + value_pair_t("\"foo bar\"", "foo bar"), + value_pair_t("\"foo\tbar\"", "foo\tbar"), + value_pair_t("\"foo's bar\"", "foo's bar"), + value_pair_t("\"foo:bar\"", "foo:bar"), + value_pair_t("\"foo,bar\"", "foo,bar"), + value_pair_t("\"foo]bar\"", "foo]bar"), + value_pair_t("\"foo}bar\"", "foo}bar"), + value_pair_t("\"\\x30\\x2B\\x6d\"", "0+m"), + + value_pair_t("\"foo\nbar\"", "foo bar"), + value_pair_t("\"foo \t\n \tbar\"", "foo bar"), + value_pair_t("\"foo\n\n \t\nbar\"", "foo\n\nbar"), + value_pair_t("\"\nfoo\n\n \t\nbar\"", " foo\n\nbar"), + value_pair_t("\"foo\nbar\n\"", "foo bar "), + value_pair_t("\"foo\\\nbar\"", "foobar"), + value_pair_t("\"foo \t\\\nbar\"", "foo \tbar"), + value_pair_t("\"\\\n foo \t\\\n\tbar\t \t\\\n\"", "foo \tbar\t \t")); + + fkyaml::detail::lexical_analyzer lexer(value_pair.first); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == value_pair.second); -} - -TEST_CASE("LexicalAnalyzer_SingleQuotedString") { - using value_pair_t = std::pair; - auto value_pair = GENERATE( - value_pair_t(std::string("\'\'"), fkyaml::node::string_type("")), - value_pair_t(std::string("\'foo\"bar\'"), fkyaml::node::string_type("foo\"bar")), - value_pair_t(std::string("\'foo bar\'"), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\'foo\'\'bar\'"), fkyaml::node::string_type("foo\'bar")), - value_pair_t(std::string("\'foo,bar\'"), fkyaml::node::string_type("foo,bar")), - value_pair_t(std::string("\'foo]bar\'"), fkyaml::node::string_type("foo]bar")), - value_pair_t(std::string("\'foo}bar\'"), fkyaml::node::string_type("foo}bar")), - value_pair_t(std::string("\'foo\"bar\'"), fkyaml::node::string_type("foo\"bar")), - value_pair_t(std::string("\'foo:bar\'"), fkyaml::node::string_type("foo:bar")), - value_pair_t(std::string("\'foo\\bar\'"), fkyaml::node::string_type("foo\\bar")), - - value_pair_t(std::string("\'foo\nbar\'"), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\'foo \t\n \tbar\'"), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\'foo\n\n \t\nbar\'"), fkyaml::node::string_type("foo\n\nbar")), - value_pair_t(std::string("\'\nfoo\n\n \t\nbar\'"), fkyaml::node::string_type(" foo\n\nbar")), - value_pair_t(std::string("\'foo\nbar\n\'"), fkyaml::node::string_type("foo bar "))); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == value_pair.second); -} - -TEST_CASE("LexicalAnalyzer_DoubleQuotedString") { - using value_pair_t = std::pair; - auto value_pair = GENERATE( - value_pair_t(std::string("\"\""), fkyaml::node::string_type("")), - value_pair_t(std::string("\"foo bar\""), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\"foo\tbar\""), fkyaml::node::string_type("foo\tbar")), - value_pair_t(std::string("\"foo's bar\""), fkyaml::node::string_type("foo's bar")), - value_pair_t(std::string("\"foo:bar\""), fkyaml::node::string_type("foo:bar")), - value_pair_t(std::string("\"foo,bar\""), fkyaml::node::string_type("foo,bar")), - value_pair_t(std::string("\"foo]bar\""), fkyaml::node::string_type("foo]bar")), - value_pair_t(std::string("\"foo}bar\""), fkyaml::node::string_type("foo}bar")), - value_pair_t(std::string("\"\\x30\\x2B\\x6d\""), fkyaml::node::string_type("0+m")), - - value_pair_t(std::string("\"foo\nbar\""), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\"foo \t\n \tbar\""), fkyaml::node::string_type("foo bar")), - value_pair_t(std::string("\"foo\n\n \t\nbar\""), fkyaml::node::string_type("foo\n\nbar")), - value_pair_t(std::string("\"\nfoo\n\n \t\nbar\""), fkyaml::node::string_type(" foo\n\nbar")), - value_pair_t(std::string("\"foo\nbar\n\""), fkyaml::node::string_type("foo bar ")), - value_pair_t(std::string("\"foo\\\nbar\""), fkyaml::node::string_type("foobar")), - value_pair_t(std::string("\"foo \t\\\nbar\""), fkyaml::node::string_type("foo \tbar")), - value_pair_t(std::string("\"\\\n foo \t\\\n\tbar\t \t\\\n\""), fkyaml::node::string_type("foo \tbar\t \t"))); - - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; - - REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == value_pair.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::DOUBLE_QUOTED_SCALAR); + REQUIRE(token.str == value_pair.second); } TEST_CASE("LexicalAnalyzer_MultiByteCharString") { @@ -634,267 +467,101 @@ TEST_CASE("LexicalAnalyzer_MultiByteCharString") { char_traits_t::to_char_type(0xBF), char_traits_t::to_char_type(0xBF)}); - lexer_t lexer(fkyaml::detail::input_adapter(mb_char)); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::str_view input(mb_char); + fkyaml::detail::lexical_analyzer lexer(input); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == mb_char); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == input); } TEST_CASE("LexicalAnalyzer_EscapedUnicodeCharacter") { - using value_pair_t = std::pair; + using value_pair_t = std::pair; using char_traits_t = std::char_traits; auto value_pair = GENERATE( - value_pair_t(std::string("\"\\x00\""), std::string {char_traits_t::to_char_type(0x00)}), - value_pair_t(std::string("\"\\x40\""), std::string {char_traits_t::to_char_type(0x40)}), - value_pair_t(std::string("\"\\x7F\""), std::string {char_traits_t::to_char_type(0x7F)}), - value_pair_t(std::string("\"\\u0000\""), std::string {char_traits_t::to_char_type(0x00)}), - value_pair_t(std::string("\"\\u0040\""), std::string {char_traits_t::to_char_type(0x40)}), - value_pair_t(std::string("\"\\u007F\""), std::string {char_traits_t::to_char_type(0x7F)}), - value_pair_t( - std::string("\"\\u0080\""), - std::string {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0x80)}), - value_pair_t( - std::string("\"\\u0400\""), - std::string {char_traits_t::to_char_type(0xD0), char_traits_t::to_char_type(0x80)}), + value_pair_t("\"\\x00\"", {char_traits_t::to_char_type(0x00)}), + value_pair_t("\"\\x40\"", {char_traits_t::to_char_type(0x40)}), + value_pair_t("\"\\x7F\"", {char_traits_t::to_char_type(0x7F)}), + value_pair_t("\"\\u0000\"", {char_traits_t::to_char_type(0x00)}), + value_pair_t("\"\\u0040\"", {char_traits_t::to_char_type(0x40)}), + value_pair_t("\"\\u007F\"", {char_traits_t::to_char_type(0x7F)}), + value_pair_t("\"\\u0080\"", {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0x80)}), + value_pair_t("\"\\u0400\"", {char_traits_t::to_char_type(0xD0), char_traits_t::to_char_type(0x80)}), + value_pair_t("\"\\u07FF\"", {char_traits_t::to_char_type(0xDF), char_traits_t::to_char_type(0xBF)}), value_pair_t( - std::string("\"\\u07FF\""), - std::string {char_traits_t::to_char_type(0xDF), char_traits_t::to_char_type(0xBF)}), + "\"\\u0800\"", + {char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0xA0), char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\u0800\""), - std::string { - char_traits_t::to_char_type(0xE0), - char_traits_t::to_char_type(0xA0), - char_traits_t::to_char_type(0x80)}), + "\"\\u8000\"", + {char_traits_t::to_char_type(0xE8), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\u8000\""), - std::string { - char_traits_t::to_char_type(0xE8), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}), + "\"\\uFFFF\"", + {char_traits_t::to_char_type(0xEF), char_traits_t::to_char_type(0xBF), char_traits_t::to_char_type(0xBF)}), + value_pair_t("\"\\U00000000\"", {char_traits_t::to_char_type(0x00)}), + value_pair_t("\"\\U00000040\"", {char_traits_t::to_char_type(0x40)}), + value_pair_t("\"\\U0000007F\"", {char_traits_t::to_char_type(0x7F)}), + value_pair_t("\"\\U00000080\"", {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0x80)}), + value_pair_t("\"\\U00000400\"", {char_traits_t::to_char_type(0xD0), char_traits_t::to_char_type(0x80)}), + value_pair_t("\"\\U000007FF\"", {char_traits_t::to_char_type(0xDF), char_traits_t::to_char_type(0xBF)}), value_pair_t( - std::string("\"\\uFFFF\""), - std::string { - char_traits_t::to_char_type(0xEF), - char_traits_t::to_char_type(0xBF), - char_traits_t::to_char_type(0xBF)}), - value_pair_t(std::string("\"\\U00000000\""), std::string {char_traits_t::to_char_type(0x00)}), - value_pair_t(std::string("\"\\U00000040\""), std::string {char_traits_t::to_char_type(0x40)}), - value_pair_t(std::string("\"\\U0000007F\""), std::string {char_traits_t::to_char_type(0x7F)}), + "\"\\U00000800\"", + {char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0xA0), char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\U00000080\""), - std::string {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0x80)}), + "\"\\U00008000\"", + {char_traits_t::to_char_type(0xE8), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\U00000400\""), - std::string {char_traits_t::to_char_type(0xD0), char_traits_t::to_char_type(0x80)}), + "\"\\U0000FFFF\"", + {char_traits_t::to_char_type(0xEF), char_traits_t::to_char_type(0xBF), char_traits_t::to_char_type(0xBF)}), value_pair_t( - std::string("\"\\U000007FF\""), - std::string {char_traits_t::to_char_type(0xDF), char_traits_t::to_char_type(0xBF)}), + "\"\\U00010000\"", + {char_traits_t::to_char_type(0xF0), + char_traits_t::to_char_type(0x90), + char_traits_t::to_char_type(0x80), + char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\U00000800\""), - std::string { - char_traits_t::to_char_type(0xE0), - char_traits_t::to_char_type(0xA0), - char_traits_t::to_char_type(0x80)}), + "\"\\U00080000\"", + {char_traits_t::to_char_type(0xF2), + char_traits_t::to_char_type(0x80), + char_traits_t::to_char_type(0x80), + char_traits_t::to_char_type(0x80)}), value_pair_t( - std::string("\"\\U00008000\""), - std::string { - char_traits_t::to_char_type(0xE8), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}), - value_pair_t( - std::string("\"\\U0000FFFF\""), - std::string { - char_traits_t::to_char_type(0xEF), - char_traits_t::to_char_type(0xBF), - char_traits_t::to_char_type(0xBF)}), - value_pair_t( - std::string("\"\\U00010000\""), - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}), - value_pair_t( - std::string("\"\\U00080000\""), - std::string { - char_traits_t::to_char_type(0xF2), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}), - value_pair_t( - std::string("\"\\U0010FFFF\""), - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x8F), - char_traits_t::to_char_type(0xBF), - char_traits_t::to_char_type(0xBF)})); + "\"\\U0010FFFF\"", + {char_traits_t::to_char_type(0xF4), + char_traits_t::to_char_type(0x8F), + char_traits_t::to_char_type(0xBF), + char_traits_t::to_char_type(0xBF)})); - lexer_t lexer(fkyaml::detail::input_adapter(value_pair.first)); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer(value_pair.first); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == value_pair.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::DOUBLE_QUOTED_SCALAR); + REQUIRE(token.str == value_pair.second); } TEST_CASE("LexicalAnalyzer_InvalidString") { SECTION("parse error") { auto buffer = GENERATE( - std::string("\"test"), - std::string("\'test"), - std::string("\"\\xw\""), - std::string("\"\\x+\""), - std::string("\"\\x=\""), - std::string("\"\\x^\""), - std::string("\"\\x{\""), - std::string("\"\\Q\"")); - - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::str_view("\"test"), + fkyaml::detail::str_view("\'test"), + fkyaml::detail::str_view("\"\\xw\""), + fkyaml::detail::str_view("\"\\x+\""), + fkyaml::detail::str_view("\"\\x=\""), + fkyaml::detail::str_view("\"\\x^\""), + fkyaml::detail::str_view("\"\\x{\""), + fkyaml::detail::str_view("\"\\Q\"")); + + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } SECTION("invalid encoding") { - std::string buffer = "\"\\U00110000\""; - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer("\"\\U00110000\""); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::invalid_encoding); } } -TEST_CASE("LexicalAnalyzer_InvalidMultiByteCharString") { - using char_traits_t = std::char_traits; - auto mb_char = GENERATE( - std::string {char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x80)}, - std::string {char_traits_t::to_char_type(0xC1), char_traits_t::to_char_type(0x80)}, - std::string {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0x7F)}, - std::string {char_traits_t::to_char_type(0xC2), char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0x7F), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0xC0), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xE0), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xED), char_traits_t::to_char_type(0x7F), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xED), char_traits_t::to_char_type(0xA0), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xED), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xED), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xEE), char_traits_t::to_char_type(0x7F), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xEE), char_traits_t::to_char_type(0xC0), char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xEF), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xEF), char_traits_t::to_char_type(0x80), char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x8F), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0xC0), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0x7F), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0xC0), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xF0), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0x7F), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0xC0), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x7F), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0xC0), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xF1), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x7F), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x90), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x7F), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0xC0), - char_traits_t::to_char_type(0x80)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x7F)}, - std::string { - char_traits_t::to_char_type(0xF4), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0xC0)}, - std::string { - char_traits_t::to_char_type(0xF5), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80), - char_traits_t::to_char_type(0x80)}); - - auto input_adapter = fkyaml::detail::input_adapter(mb_char); - REQUIRE_THROWS_AS(lexer_t(std::move(input_adapter)), fkyaml::invalid_encoding); -} - TEST_CASE("LexicalAnalyzer_UnescapedControlCharacter") { auto unescaped_char = GENERATE( char(0x01), @@ -928,48 +595,48 @@ TEST_CASE("LexicalAnalyzer_UnescapedControlCharacter") { std::string buffer("test"); buffer.push_back(unescaped_char); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("empty literal string scalar with strip chomping") { const char input[] = "|-\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == ""); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == ""); } SECTION("empty literal string scalar with clip chomping") { const char input[] = "|\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == ""); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == ""); } SECTION("empty literal string scalar with keep chomping") { const char input[] = "|+\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\n"); } SECTION("literal string scalar with 0 indent level.") { const char input[] = "|0\n" "foo"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } @@ -977,7 +644,7 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { const char input[] = "|2\n" " foo"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } @@ -985,22 +652,22 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { const char input[] = "|2\n" " foo\n" " bar\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == " foo\nbar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == " foo\nbar\n"); } SECTION("literal string scalar") { const char input[] = "|\n" " foo\n" " bar\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\nbar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\nbar\n"); } SECTION("literal string scalar with implicit indentation and strip chomping") { @@ -1011,11 +678,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\nfoo\nbar\n\nbaz"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\nfoo\nbar\n\nbaz"); } SECTION("literal string scalar with explicit indentation and strip chomping") { @@ -1025,11 +692,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n bar\n\nbaz"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n bar\n\nbaz"); } SECTION("literal string scalar with implicit indentation and clip chomping") { @@ -1040,11 +707,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\nfoo\nbar\n\nbaz\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\nfoo\nbar\n\nbaz\n"); } SECTION("literal string scalar with explicit indentation and clip chomping") { @@ -1054,11 +721,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n bar\n\nbaz\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n bar\n\nbaz\n"); } SECTION("literal string scalar with clip chomping and no trailing newlines") { @@ -1067,11 +734,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { " bar\n" "\n" " baz"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n bar\n\nbaz"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n bar\n\nbaz"); } SECTION("literal string scalar with implicit indentation and keep chomping") { @@ -1082,11 +749,11 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\nfoo\nbar\n\nbaz\n\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\nfoo\nbar\n\nbaz\n\n"); } SECTION("literal string scalar with explicit indentation and keep chomping") { @@ -1096,52 +763,78 @@ TEST_CASE("LexicalAnalyzer_LiteralStringScalar") { "\n" " baz\n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); + + REQUIRE_NOTHROW(token = lexer.get_next_token()); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n bar\n\nbaz\n\n"); + } + + SECTION("literal string scalar with trailing spaces/tabs after the block scalar header.") { + auto input = GENERATE( + fkyaml::detail::str_view("|2 \n foo\n"), + fkyaml::detail::str_view("|2\t\t\n foo\n"), + fkyaml::detail::str_view("|2 # comment\n foo\n")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n bar\n\nbaz\n\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n"); + } + + SECTION("literal string scalar with invalid block scalar headers") { + auto input = GENERATE( + fkyaml::detail::str_view("|++2\n foo"), + fkyaml::detail::str_view("|--2\n foo"), + fkyaml::detail::str_view("|+-2\n foo"), + fkyaml::detail::str_view("|-+2\n foo"), + fkyaml::detail::str_view("|+0\n foo"), + fkyaml::detail::str_view("|+11\n foo"), + fkyaml::detail::str_view("|invalid\n foo")); + + fkyaml::detail::lexical_analyzer lexer(input); + REQUIRE_THROWS_AS(token = lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_FoldedString") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("empty folded string scalar with strip chomping") { const char input[] = ">-\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == ""); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str.empty()); } SECTION("empty folded string scalar with clip chomping") { const char input[] = ">\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == ""); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == ""); } SECTION("empty folded string scalar with keep chomping") { const char input[] = ">+\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\n"); } SECTION("folded string scalar with 0 indent level") { const char input[] = "|0\n" "foo"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } @@ -1149,7 +842,7 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { const char input[] = ">2\n" " foo"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } @@ -1157,22 +850,22 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { const char input[] = ">2\n" " foo\n" " bar\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "\n foo\nbar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "\n foo\nbar\n"); } SECTION("folded string scalar with the non-first line being more indented than the indicated level") { const char input[] = ">2\n" " foo\n" " bar\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n bar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n bar\n"); } SECTION("folded string scalar") { @@ -1182,11 +875,11 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { "\n" " bar\n" " \n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo\n\nbar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n\nbar\n"); } SECTION("folded string scalar with implicit indentation and strip chomping") { @@ -1195,11 +888,11 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { " bar\n" " \n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo bar"); } SECTION("folded string scalar with implicit indentation and clip chomping") { @@ -1208,11 +901,11 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { " bar\n" " \n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo bar\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo bar\n"); } SECTION("folded string scalar with implicit indentation and keep chomping") { @@ -1221,19 +914,45 @@ TEST_CASE("LexicalAnalyzer_FoldedString") { " bar\n" " \n" "\n"; - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); + + REQUIRE_NOTHROW(token = lexer.get_next_token()); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo bar\n\n"); + } + + SECTION("folded string scalar with trailing spaces/tabs/comments after the block scalar header.") { + auto input = GENERATE( + fkyaml::detail::str_view(">2 \n foo\n"), + fkyaml::detail::str_view(">2\t\t\n foo\n"), + fkyaml::detail::str_view(">2 # comment\n foo\n")); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "foo bar\n\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "foo\n"); + } + + SECTION("folded string scalar with invalid block scalar headers") { + auto input = GENERATE( + fkyaml::detail::str_view(">++2\n foo"), + fkyaml::detail::str_view(">--2\n foo"), + fkyaml::detail::str_view(">+-2\n foo"), + fkyaml::detail::str_view(">-+2\n foo"), + fkyaml::detail::str_view(">+0\n foo"), + fkyaml::detail::str_view(">+11\n foo"), + fkyaml::detail::str_view(">invalid\n foo")); + + fkyaml::detail::lexical_analyzer lexer(input); + REQUIRE_THROWS_AS(token = lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_Anchor") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("valid anchor name") { - using test_data_t = std::pair; + using test_data_t = std::pair; auto test_data = GENERATE( test_data_t {"&anchor", "anchor"}, test_data_t {"&anchor name", "anchor"}, @@ -1247,35 +966,35 @@ TEST_CASE("LexicalAnalyzer_Anchor") { test_data_t {"&anchor: ", "anchor:"}, test_data_t {"&anchor:", "anchor:"}); - lexer_t lexer(fkyaml::detail::input_adapter(test_data.first)); + fkyaml::detail::lexical_analyzer lexer(test_data.first); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::ANCHOR_PREFIX); - REQUIRE_NOTHROW(lexer.get_string() == test_data.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::ANCHOR_PREFIX); + REQUIRE(token.str == test_data.second); } SECTION("invalid anchor name") { auto input = GENERATE( - std::string("&"), - std::string("& "), - std::string("&\t"), - std::string("&\n"), - std::string("&{"), - std::string("&}"), - std::string("&["), - std::string("&]"), - std::string("&,")); - - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("&"), + fkyaml::detail::str_view("& "), + fkyaml::detail::str_view("&\t"), + fkyaml::detail::str_view("&\n"), + fkyaml::detail::str_view("&{"), + fkyaml::detail::str_view("&}"), + fkyaml::detail::str_view("&["), + fkyaml::detail::str_view("&]"), + fkyaml::detail::str_view("&,")); + + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_Alias") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("valid anchor name") { - using test_data_t = std::pair; + using test_data_t = std::pair; auto test_data = GENERATE( test_data_t {"*anchor", "anchor"}, test_data_t {"*anchor name", "anchor"}, @@ -1289,664 +1008,605 @@ TEST_CASE("LexicalAnalyzer_Alias") { test_data_t {"*anchor: ", "anchor:"}, test_data_t {"*anchor:", "anchor:"}); - lexer_t lexer(fkyaml::detail::input_adapter(test_data.first)); + fkyaml::detail::lexical_analyzer lexer(test_data.first); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::ALIAS_PREFIX); - REQUIRE_NOTHROW(lexer.get_string() == test_data.second); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::ALIAS_PREFIX); + REQUIRE_NOTHROW(token.str == test_data.second); } SECTION("invalid anchor name") { auto input = GENERATE( - std::string("*"), - std::string("* "), - std::string("*\t"), - std::string("*\n"), - std::string("*{"), - std::string("*}"), - std::string("*["), - std::string("*]"), - std::string("*,")); - - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("*"), + fkyaml::detail::str_view("* "), + fkyaml::detail::str_view("*\t"), + fkyaml::detail::str_view("*\n"), + fkyaml::detail::str_view("*{"), + fkyaml::detail::str_view("*}"), + fkyaml::detail::str_view("*["), + fkyaml::detail::str_view("*]"), + fkyaml::detail::str_view("*,")); + + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_Tag") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("valid tag names") { auto input = GENERATE( - std::string("! tag"), - std::string("!\ntag"), - std::string("!local tag"), - std::string("!local%2A%7C tag"), - std::string("!!foo tag"), - std::string("!!foo%2A%7C tag"), - std::string("! tag"), - std::string("! tag"), - std::string("! tag"), - std::string("! tag"), - std::string("!foo!bar tag")); + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("!\ntag"), + fkyaml::detail::str_view("!local tag"), + fkyaml::detail::str_view("!local%2A%7C tag"), + fkyaml::detail::str_view("!!foo tag"), + fkyaml::detail::str_view("!!foo%2A%7C tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("!foo!bar tag")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::TAG_PREFIX); - REQUIRE(lexer.get_string() == input.substr(0, input.size() - 4)); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::TAG_PREFIX); + REQUIRE(token.str == input.substr(0, input.size() - 4)); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE(lexer.get_string() == "tag"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "tag"); } SECTION("valid tag name (not followed by a value)") { - auto input = GENERATE(std::string("!"), std::string("!!foo"), std::string("!foo!bar"), std::string("!foo")); + auto input = GENERATE( + fkyaml::detail::str_view("!"), + fkyaml::detail::str_view("!!foo"), + fkyaml::detail::str_view("!foo!bar"), + fkyaml::detail::str_view("!foo")); - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::TAG_PREFIX); - REQUIRE(lexer.get_string() == input); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::TAG_PREFIX); + REQUIRE(token.str == input); } SECTION("invalid tag names") { auto input = GENERATE( - std::string("!!f!oo tag"), - std::string("! tag"), - std::string("! tag"), - std::string("!<%f:oo> tag"), - std::string("! tag"), - std::string("!foo! tag"), - std::string("!foo!%f:oo tag")); - - lexer_t lexer(fkyaml::detail::input_adapter(input)); + fkyaml::detail::str_view("!!f!oo tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("!<%f:oo> tag"), + fkyaml::detail::str_view("! tag"), + fkyaml::detail::str_view("!foo! tag"), + fkyaml::detail::str_view("!foo!%f:oo tag")); + + fkyaml::detail::lexical_analyzer lexer(input); REQUIRE_THROWS_AS(token = lexer.get_next_token(), fkyaml::parse_error); } } TEST_CASE("LexicalAnalyzer_ReservedIndicator") { - auto buffer = GENERATE(std::string("@invalid"), std::string("`invalid")); - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + auto buffer = GENERATE(fkyaml::detail::str_view("@invalid"), fkyaml::detail::str_view("`invalid")); + fkyaml::detail::lexical_analyzer lexer(buffer); REQUIRE_THROWS_AS(lexer.get_next_token(), fkyaml::parse_error); } TEST_CASE("LexicalAnalyzer_KeyBooleanValuePair") { - lexer_t lexer(fkyaml::detail::input_adapter("test: true")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("test: true"); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_KeyIntegerValuePair") { - lexer_t lexer(fkyaml::detail::input_adapter("test: -5784")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("test: -5784"); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::INTEGER_VALUE); - REQUIRE_NOTHROW(lexer.get_integer()); - REQUIRE(lexer.get_integer() == -5784); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "-5784"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_KeyFloatNumberValuePair") { - lexer_t lexer(fkyaml::detail::input_adapter("test: -5.58e-3")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("test: -5.58e-3"); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == -5.58e-3); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "-5.58e-3"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_KeyStringValuePair") { - lexer_t lexer(fkyaml::detail::input_adapter("test: \"some value\"")); - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_analyzer lexer("test: \"some value\""); + fkyaml::detail::lexical_token token; REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("some value") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::DOUBLE_QUOTED_SCALAR); + REQUIRE(token.str == "some value"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } TEST_CASE("LexicalAnalyzer_FlowSequence") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("simple flow sequence") { - lexer_t lexer(fkyaml::detail::input_adapter("test: [ foo, bar ]")); + fkyaml::detail::lexical_analyzer lexer("test: [ foo, bar ]"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("flow sequence with flow mapping child nodes") { - lexer_t lexer(fkyaml::detail::input_adapter("test: [ { foo: one, bar: false }, { foo: two, bar: true } ]")); + fkyaml::detail::lexical_analyzer lexer("test: [ { foo: one, bar: false }, { foo: two, bar: true } ]"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("one") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "one"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == false); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "false"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("two") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "two"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } } TEST_CASE("LexicalAnalyzer_FlowMapping") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("simple flow mapping") { - lexer_t lexer(fkyaml::detail::input_adapter("test: { bool: true, foo: bar, pi: 3.14 }")); + fkyaml::detail::lexical_analyzer lexer("test: { bool : true, foo :b: bar, pi: 3.14 }"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bool") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bool"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo :b"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::VALUE_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("pi") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "pi"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == 3.14); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("flow maping with a child mapping node") { - lexer_t lexer(fkyaml::detail::input_adapter("test: {foo: bar baz}")); + fkyaml::detail::lexical_analyzer lexer("test: {foo: bar baz}"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_BEGIN); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar baz") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar baz"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::MAPPING_FLOW_END); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } } TEST_CASE("LexicalAnalyzer_BlockSequence") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("simple block sequence") { - std::string buffer = "test:\n - foo\n - bar"; - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer("test:\n - foo\n - bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "test"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("block sequence with block mapping child nodes") { - std::string buffer = "test:\n - foo: one\n bar: false\n - foo: two\n bar: true"; - lexer_t lexer(fkyaml::detail::input_adapter(buffer)); + fkyaml::detail::lexical_analyzer lexer("test:\n - foo: one\n bar: false\n - foo: two\n bar: true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "test"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "one"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "one"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == false); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "false"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SEQUENCE_BLOCK_PREFIX); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "two"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "two"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } } TEST_CASE("LexicalAnalyzer_BlockMapping") { - fkyaml::detail::lexical_token_t token; + fkyaml::detail::lexical_token token; SECTION("simple block mapping") { - lexer_t lexer(fkyaml::detail::input_adapter("test:\n bool: true\n foo: \'bar\'\n pi: 3.14")); + fkyaml::detail::lexical_analyzer lexer("test:\n bool: true\n foo: \'bar\'\n pi: 3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("test") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bool") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "bool"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); - REQUIRE_NOTHROW(lexer.get_boolean()); - REQUIRE(lexer.get_boolean() == true); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "true"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("foo") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("bar") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SINGLE_QUOTED_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string().compare("pi") == 0); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "pi"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == 3.14); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("block mapping with a literal string scalar value") { - lexer_t lexer(fkyaml::detail::input_adapter("test: |\n a literal scalar.\nfoo: \'bar\'\npi: 3.14")); + fkyaml::detail::lexical_analyzer lexer("test: |\n a literal scalar.\nfoo: \'bar\'\npi: 3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "test"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "a literal scalar.\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "a literal scalar.\n"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SINGLE_QUOTED_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "pi"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "pi"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == 3.14); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } SECTION("block mapping with a folded string scalar value") { - lexer_t lexer(fkyaml::detail::input_adapter("test: >\n a literal scalar.\nfoo: \'bar\'\npi: 3.14")); + fkyaml::detail::lexical_analyzer lexer("test: >\n a literal scalar.\nfoo: \'bar\'\npi: 3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "test"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "test"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "a literal scalar.\n"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::BLOCK_SCALAR); + REQUIRE(token.str == "a literal scalar.\n"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "foo"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "foo"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "bar"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::SINGLE_QUOTED_SCALAR); + REQUIRE(token.str == "bar"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::STRING_VALUE); - REQUIRE_NOTHROW(lexer.get_string()); - REQUIRE(lexer.get_string() == "pi"); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "pi"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::KEY_SEPARATOR); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); - REQUIRE_NOTHROW(lexer.get_float_number()); - REQUIRE(lexer.get_float_number() == 3.14); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::PLAIN_SCALAR); + REQUIRE(token.str == "3.14"); REQUIRE_NOTHROW(token = lexer.get_next_token()); - REQUIRE(token == fkyaml::detail::lexical_token_t::END_OF_BUFFER); + REQUIRE(token.type == fkyaml::detail::lexical_token_t::END_OF_BUFFER); } } diff --git a/test/unit_test/test_node_attrs.cpp b/test/unit_test/test_node_attrs.cpp new file mode 100644 index 00000000..0d82e80d --- /dev/null +++ b/test/unit_test/test_node_attrs.cpp @@ -0,0 +1,51 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include + +#include + +TEST_CASE("NodeAttrs_FromNodeType") { + using test_data_t = std::pair; + auto test_data = GENERATE( + test_data_t {fkyaml::node_type::SEQUENCE, fkyaml::detail::node_attr_bits::seq_bit}, + test_data_t {fkyaml::node_type::MAPPING, fkyaml::detail::node_attr_bits::map_bit}, + test_data_t {fkyaml::node_type::NULL_OBJECT, fkyaml::detail::node_attr_bits::null_bit}, + test_data_t {fkyaml::node_type::BOOLEAN, fkyaml::detail::node_attr_bits::bool_bit}, + test_data_t {fkyaml::node_type::INTEGER, fkyaml::detail::node_attr_bits::int_bit}, + test_data_t {fkyaml::node_type::FLOAT, fkyaml::detail::node_attr_bits::float_bit}, + test_data_t {fkyaml::node_type::STRING, fkyaml::detail::node_attr_bits::string_bit}); + REQUIRE(fkyaml::detail::node_attr_bits::from_node_type(test_data.first) == test_data.second); +} + +TEST_CASE("NodeAttrs_ToNodeType") { + using test_data_t = std::pair; + auto test_data = GENERATE( + test_data_t {fkyaml::detail::node_attr_bits::seq_bit, fkyaml::node_type::SEQUENCE}, + test_data_t {fkyaml::detail::node_attr_bits::map_bit, fkyaml::node_type::MAPPING}, + test_data_t {fkyaml::detail::node_attr_bits::null_bit, fkyaml::node_type::NULL_OBJECT}, + test_data_t {fkyaml::detail::node_attr_bits::bool_bit, fkyaml::node_type::BOOLEAN}, + test_data_t {fkyaml::detail::node_attr_bits::int_bit, fkyaml::node_type::INTEGER}, + test_data_t {fkyaml::detail::node_attr_bits::float_bit, fkyaml::node_type::FLOAT}, + test_data_t {fkyaml::detail::node_attr_bits::string_bit, fkyaml::node_type::STRING}); + REQUIRE(fkyaml::detail::node_attr_bits::to_node_type(test_data.first) == test_data.second); +} + +TEST_CASE("NodeAttrs_GetAnchorOffset") { + using test_data_t = std::pair; + auto test_data = GENERATE(test_data_t {0, 0}, test_data_t {0xA0000000u, 0x28u}, test_data_t {0xFC000000u, 0x3Fu}); + REQUIRE(fkyaml::detail::node_attr_bits::get_anchor_offset(test_data.first) == test_data.second); +} + +TEST_CASE("NodeAttrs_SetAnchorOffset") { + using test_data_t = std::pair; + auto test_data = GENERATE(test_data_t {0, 0}, test_data_t {0x28u, 0xA0000000u}, test_data_t {0x3Fu, 0xFC000000u}); + fkyaml::detail::node_attr_t attrs = 0; + fkyaml::detail::node_attr_bits::set_anchor_offset(test_data.first, attrs); + REQUIRE(attrs == test_data.second); +} diff --git a/test/unit_test/test_node_class.cpp b/test/unit_test/test_node_class.cpp index c8384e8f..00945f22 100644 --- a/test/unit_test/test_node_class.cpp +++ b/test/unit_test/test_node_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -33,41 +33,82 @@ TEST_CASE("Node_DefaultCtor") { } TEST_CASE("Node_SequenceTypeCtor") { - fkyaml::node node(fkyaml::node::node_t::SEQUENCE); + fkyaml::node node(fkyaml::node_type::SEQUENCE); REQUIRE(node.is_sequence()); REQUIRE(node.size() == 0); } TEST_CASE("Node_MappingTypeCtor") { - fkyaml::node node(fkyaml::node::node_t::MAPPING); + fkyaml::node node(fkyaml::node_type::MAPPING); REQUIRE(node.is_mapping()); REQUIRE(node.size() == 0); } TEST_CASE("Node_NullTypeCtor") { - fkyaml::node node(fkyaml::node::node_t::NULL_OBJECT); + fkyaml::node node(fkyaml::node_type::NULL_OBJECT); REQUIRE(node.is_null()); } TEST_CASE("Node_BooleanTypeCtor") { - fkyaml::node node(fkyaml::node::node_t::BOOLEAN); + fkyaml::node node(fkyaml::node_type::BOOLEAN); REQUIRE(node.is_boolean()); REQUIRE(node.get_value_ref() == false); } TEST_CASE("Node_IntegerTypeCtor") { + fkyaml::node node(fkyaml::node_type::INTEGER); + REQUIRE(node.is_integer()); + REQUIRE(node.get_value_ref() == 0); +} + +TEST_CASE("Node_FloatTypeCtor") { + fkyaml::node node(fkyaml::node_type::FLOAT); + REQUIRE(node.is_float_number()); + REQUIRE(node.get_value_ref() == 0.0); +} + +TEST_CASE("Node_StringTypeCtor") { + fkyaml::node node(fkyaml::node_type::STRING); + REQUIRE(node.is_string()); + REQUIRE(node.size() == 0); +} + +TEST_CASE("Node_SequenceTypeCtor(deprecated)") { + fkyaml::node node(fkyaml::node::node_t::SEQUENCE); + REQUIRE(node.is_sequence()); + REQUIRE(node.size() == 0); +} + +TEST_CASE("Node_MappingTypeCtor(deprecated)") { + fkyaml::node node(fkyaml::node::node_t::MAPPING); + REQUIRE(node.is_mapping()); + REQUIRE(node.size() == 0); +} + +TEST_CASE("Node_NullTypeCtor(deprecated)") { + fkyaml::node node(fkyaml::node::node_t::NULL_OBJECT); + REQUIRE(node.is_null()); +} + +TEST_CASE("Node_BooleanTypeCtor(deprecated)") { + fkyaml::node node(fkyaml::node::node_t::BOOLEAN); + REQUIRE(node.is_boolean()); + REQUIRE(node.get_value_ref() == false); +} + +TEST_CASE("Node_IntegerTypeCtor(deprecated)") { fkyaml::node node(fkyaml::node::node_t::INTEGER); REQUIRE(node.is_integer()); REQUIRE(node.get_value_ref() == 0); } -TEST_CASE("Node_FloatNumberTypeCtor") { +TEST_CASE("Node_FloatNumberTypeCtor(deprecated)") { fkyaml::node node(fkyaml::node::node_t::FLOAT_NUMBER); REQUIRE(node.is_float_number()); REQUIRE(node.get_value_ref() == 0.0); } -TEST_CASE("Node_StringTypeCtor") { +TEST_CASE("Node_StringTypeCtor(deprecated)") { fkyaml::node node(fkyaml::node::node_t::STRING); REQUIRE(node.is_string()); REQUIRE(node.size() == 0); @@ -81,12 +122,12 @@ TEST_CASE("Node_ThrowingSpecializationTypeCtor") { }; using NodeType = fkyaml::basic_node; - REQUIRE_THROWS_AS(NodeType(NodeType::node_t::STRING), fkyaml::exception); + REQUIRE_THROWS_AS(NodeType(fkyaml::node_type::STRING), fkyaml::exception); } TEST_CASE("Node_SequenceCtor") { fkyaml::node node(fkyaml::node::sequence_type {fkyaml::node(true), fkyaml::node(false)}); - REQUIRE(node.type() == fkyaml::node::node_t::SEQUENCE); + REQUIRE(node.get_type() == fkyaml::node_type::SEQUENCE); REQUIRE(node.is_sequence()); REQUIRE(node.size() == 2); REQUIRE(node[0].is_boolean()); @@ -97,7 +138,7 @@ TEST_CASE("Node_SequenceCtor") { TEST_CASE("Node_MappingCtor") { fkyaml::node node(fkyaml::node::mapping_type {{"test", fkyaml::node(true)}}); - REQUIRE(node.type() == fkyaml::node::node_t::MAPPING); + REQUIRE(node.get_type() == fkyaml::node_type::MAPPING); REQUIRE(node.is_mapping()); REQUIRE(node.size() == 1); REQUIRE(node.contains("test")); @@ -107,34 +148,34 @@ TEST_CASE("Node_MappingCtor") { TEST_CASE("Node_NullCtor") { fkyaml::node node(nullptr); - REQUIRE(node.type() == fkyaml::node::node_t::NULL_OBJECT); + REQUIRE(node.get_type() == fkyaml::node_type::NULL_OBJECT); REQUIRE(node.is_null()); } TEST_CASE("Node_BooleanCtor") { fkyaml::node node(true); - REQUIRE(node.type() == fkyaml::node::node_t::BOOLEAN); + REQUIRE(node.get_type() == fkyaml::node_type::BOOLEAN); REQUIRE(node.is_boolean()); REQUIRE(node.get_value_ref() == true); } TEST_CASE("Node_IntegerCtor") { fkyaml::node node(23467); - REQUIRE(node.type() == fkyaml::node::node_t::INTEGER); + REQUIRE(node.get_type() == fkyaml::node_type::INTEGER); REQUIRE(node.is_integer()); REQUIRE(node.get_value_ref() == 23467); } TEST_CASE("Node_FloatNumberCtor") { fkyaml::node node(3.14); - REQUIRE(node.type() == fkyaml::node::node_t::FLOAT_NUMBER); + REQUIRE(node.get_type() == fkyaml::node_type::FLOAT); REQUIRE(node.is_float_number()); REQUIRE(node.get_value_ref() == 3.14); } TEST_CASE("Node_StringCtor") { auto node = GENERATE(fkyaml::node(std::string("test"))); - REQUIRE(node.type() == fkyaml::node::node_t::STRING); + REQUIRE(node.get_type() == fkyaml::node_type::STRING); REQUIRE(node.is_string()); REQUIRE(node.size() == 4); REQUIRE(node.get_value_ref() == "test"); @@ -144,7 +185,7 @@ TEST_CASE("Node_StringCtor") { TEST_CASE("Node_StringViewCtor") { using namespace std::string_view_literals; auto node = fkyaml::node("test"sv); - REQUIRE(node.type() == fkyaml::node::node_t::STRING); + REQUIRE(node.get_type() == fkyaml::node_type::STRING); REQUIRE(node.is_string()); REQUIRE(node.size() == 4); REQUIRE(node.get_value_ref() == "test"); @@ -1391,7 +1432,29 @@ TEST_CASE("Node_GreaterThanOrEqualToOperator") { // test cases for type property getter/checkers // -TEST_CASE("Node_Type") { +TEST_CASE("Node_GetType") { + using NodeTypePair = std::pair; + auto type_pair = GENERATE( + NodeTypePair(fkyaml::node::sequence(), fkyaml::node_type::SEQUENCE), + NodeTypePair(fkyaml::node::mapping(), fkyaml::node_type::MAPPING), + NodeTypePair(fkyaml::node(), fkyaml::node_type::NULL_OBJECT), + NodeTypePair(fkyaml::node(false), fkyaml::node_type::BOOLEAN), + NodeTypePair(fkyaml::node(0), fkyaml::node_type::INTEGER), + NodeTypePair(fkyaml::node(0.0), fkyaml::node_type::FLOAT), + NodeTypePair(fkyaml::node(""), fkyaml::node_type::STRING)); + + SECTION("non-alias node types") { + REQUIRE(type_pair.first.get_type() == type_pair.second); + } + + SECTION("alias node types") { + type_pair.first.add_anchor_name("anchor_name"); + fkyaml::node alias = fkyaml::node::alias_of(type_pair.first); + REQUIRE(alias.get_type() == type_pair.second); + } +} + +TEST_CASE("Node_Type(deprecated)") { using NodeTypePair = std::pair; auto type_pair = GENERATE( NodeTypePair(fkyaml::node::sequence(), fkyaml::node::node_t::SEQUENCE), @@ -2095,7 +2158,24 @@ TEST_CASE("Node_At") { // test cases for YAML version property getter/setter // -TEST_CASE("Node_SetYamlVersion") { +TEST_CASE("Node_SetYamlVersionType") { + fkyaml::node node; + node.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_1); + REQUIRE(node.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1); + + node.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_2); + REQUIRE(node.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); +} + +TEST_CASE("Node_GetYamlVersionType") { + fkyaml::node node; + REQUIRE(node.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_2); + + node.set_yaml_version_type(fkyaml::yaml_version_type::VERSION_1_1); + REQUIRE(node.get_yaml_version_type() == fkyaml::yaml_version_type::VERSION_1_1); +} + +TEST_CASE("Node_SetYamlVersion(deprecated)") { fkyaml::node node; node.set_yaml_version(fkyaml::node::yaml_version_t::VER_1_1); REQUIRE(node.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_1); @@ -2104,7 +2184,7 @@ TEST_CASE("Node_SetYamlVersion") { REQUIRE(node.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); } -TEST_CASE("Node_GetYamlVersion") { +TEST_CASE("Node_GetYamlVersion(deprecated)") { fkyaml::node node; REQUIRE(node.get_yaml_version() == fkyaml::node::yaml_version_t::VER_1_2); diff --git a/test/unit_test/test_node_ref_storage_class.cpp b/test/unit_test/test_node_ref_storage_class.cpp index 6d563e5f..676be5dc 100644 --- a/test/unit_test/test_node_ref_storage_class.cpp +++ b/test/unit_test/test_node_ref_storage_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_node_type.cpp b/test/unit_test/test_node_type.cpp new file mode 100644 index 00000000..cd40d73f --- /dev/null +++ b/test/unit_test/test_node_type.cpp @@ -0,0 +1,26 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include + +#include + +#include + +TEST_CASE("NodeType_ToString") { + using test_data_t = std::pair; + auto test_data = GENERATE( + test_data_t {fkyaml::node_type::SEQUENCE, "SEQUENCE"}, + test_data_t {fkyaml::node_type::MAPPING, "MAPPING"}, + test_data_t {fkyaml::node_type::NULL_OBJECT, "NULL_OBJECT"}, + test_data_t {fkyaml::node_type::BOOLEAN, "BOOLEAN"}, + test_data_t {fkyaml::node_type::INTEGER, "INTEGER"}, + test_data_t {fkyaml::node_type::FLOAT, "FLOAT"}, + test_data_t {fkyaml::node_type::STRING, "STRING"}); + REQUIRE(test_data.second == fkyaml::to_string(test_data.first)); +} diff --git a/test/unit_test/test_ordered_map_class.cpp b/test/unit_test/test_ordered_map_class.cpp index 7005f04e..e11d535c 100644 --- a/test/unit_test/test_ordered_map_class.cpp +++ b/test/unit_test/test_ordered_map_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_position_tracker_class.cpp b/test/unit_test/test_position_tracker_class.cpp index 7ade9ab1..82bb082e 100644 --- a/test/unit_test/test_position_tracker_class.cpp +++ b/test/unit_test/test_position_tracker_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -13,7 +13,7 @@ #include TEST_CASE("PositionTracker_InitialState") { - std::string input = "test"; + fkyaml::detail::str_view input = "test"; fkyaml::detail::position_tracker pos_tracker {}; pos_tracker.set_target_buffer(input); @@ -26,7 +26,7 @@ TEST_CASE("PositionTracker_MultipleLines") { fkyaml::detail::position_tracker pos_tracker {}; SECTION("first character is not a newline code") { - std::string input = "test\nfoo"; + fkyaml::detail::str_view input = "test\nfoo"; pos_tracker.set_target_buffer(input); REQUIRE(pos_tracker.get_cur_pos() == 0); @@ -60,7 +60,7 @@ TEST_CASE("PositionTracker_MultipleLines") { } SECTION("first character is a newline code") { - std::string input = "\ntest\nfoo"; + fkyaml::detail::str_view input = "\ntest\nfoo"; pos_tracker.set_target_buffer(input); REQUIRE(pos_tracker.get_cur_pos() == 0); diff --git a/test/unit_test/test_scalar_conv.cpp b/test/unit_test/test_scalar_conv.cpp new file mode 100644 index 00000000..757764f2 --- /dev/null +++ b/test/unit_test/test_scalar_conv.cpp @@ -0,0 +1,465 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include + +#include + +TEST_CASE("ScalarConv_aton") { + std::nullptr_t null = nullptr; + + SECTION("valid string for the null value") { + auto input = GENERATE(std::string("null"), std::string("Null"), std::string("NULL"), std::string("~")); + REQUIRE(fkyaml::detail::aton(input.begin(), input.end(), null) == true); + } + + SECTION("invalid string for the null value") { + auto input = GENERATE(std::string("test"), std::string("")); + REQUIRE(fkyaml::detail::aton(input.begin(), input.end(), null) == false); + } +} + +TEST_CASE("ScalarConv_atob") { + bool boolean = false; + + SECTION("valid string for the true value") { + auto input = GENERATE(std::string("true"), std::string("True"), std::string("TRUE")); + REQUIRE(fkyaml::detail::atob(input.begin(), input.end(), boolean) == true); + REQUIRE(boolean == true); + } + + SECTION("valid string for the false value") { + auto input = GENERATE(std::string("false"), std::string("False"), std::string("FALSE")); + REQUIRE(fkyaml::detail::atob(input.begin(), input.end(), boolean) == true); + REQUIRE(boolean == false); + } + + SECTION("invalid string for the boolean values") { + auto input = GENERATE(std::string("test"), std::string("test2"), std::string("")); + REQUIRE(fkyaml::detail::atob(input.begin(), input.end(), boolean) == false); + } +} + +TEST_CASE("ScalarConv_atoi") { + // implement common (not integer type specific) test cases + + int32_t integer = 0; + + SECTION("empty input") { + std::string input = ""; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } + + SECTION("decimal number with an explicit plus sign") { + std::string input = "+64"; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == int32_t(64)); + } + + SECTION("hexadecimal number with different writings in alphabets") { + auto input = GENERATE(std::string("0xFA"), std::string("0xfa")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 0xFA); + } + + SECTION("max digits but within bounds") { + using test_data_t = std::pair; + auto test_data = GENERATE( + test_data_t(std::string("1147483647"), 1147483647), test_data_t(std::string("-1147483648"), -1147483648)); + + REQUIRE(fkyaml::detail::atoi(test_data.first.begin(), test_data.first.end(), integer) == true); + REQUIRE(integer == test_data.second); + } + + SECTION("invalid values") { + auto input = GENERATE( + std::string("0123"), + std::string("+"), + std::string("21474836470"), + std::string("-"), + std::string("-21474836480"), + std::string("1/"), + std::string("1:"), + std::string("0o"), + std::string("0o/"), + std::string("0o8"), + std::string("0x"), + std::string("0x/"), + std::string("0x:"), + std::string("0x@"), + std::string("0xG"), + std::string("0x`"), + std::string("0xg")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_int8_t") { + int8_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64); + } + + SECTION("negative value") { + std::string input = "-64"; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -64); + } + + SECTION("max values") { + auto input = GENERATE(std::string("127"), std::string("0o177"), std::string("0x7F")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 127); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("-128"), std::string("0o200"), std::string("0x80")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -128); + } + + SECTION("invalid values") { + auto input = GENERATE(std::string("128"), std::string("-129"), std::string("0o400"), std::string("0x100")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_uint8_t") { + uint8_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64u); + } + + SECTION("max values") { + auto input = GENERATE(std::string("255"), std::string("0o377"), std::string("0xFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 255u); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("0"), std::string("0o0"), std::string("0x0")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 0u); + } + + SECTION("invalid values") { + auto input = GENERATE(std::string("256"), std::string("-1"), std::string("0o400"), std::string("0x100")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_int16_t") { + int16_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64); + } + + SECTION("negative value") { + std::string input = "-64"; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -64); + } + + SECTION("max values") { + auto input = GENERATE(std::string("32767"), std::string("0o77777"), std::string("0x7FFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 32767); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("-32768"), std::string("0o100000"), std::string("0x8000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -32768); + } + + SECTION("invalid values") { + auto input = + GENERATE(std::string("32768"), std::string("-32769"), std::string("0o200000"), std::string("0x10000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_uint16_t") { + uint16_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64u); + } + + SECTION("max values") { + auto input = GENERATE(std::string("65535"), std::string("0o177777"), std::string("0xFFFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 65535u); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("0"), std::string("0o0"), std::string("0x0")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 0u); + } + + SECTION("invalid values") { + auto input = GENERATE(std::string("65536"), std::string("-1"), std::string("0o200000"), std::string("0x10000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_int32_t") { + int32_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64); + } + + SECTION("negative value") { + std::string input = "-64"; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -64); + } + + SECTION("max values") { + auto input = GENERATE(std::string("2147483647"), std::string("0o17777777777"), std::string("0x7FFFFFFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 2147483647); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("-2147483648"), std::string("0o20000000000"), std::string("0x80000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -2147483648); + } + + SECTION("invalid values") { + auto input = GENERATE( + std::string("2147483648"), + std::string("-2147483649"), + std::string("0o40000000000"), + std::string("0x100000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_uint32_t") { + uint32_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64u); + } + + SECTION("max values") { + auto input = GENERATE(std::string("4294967295"), std::string("0o37777777777"), std::string("0xFFFFFFFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 4294967295u); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("0"), std::string("0o0"), std::string("0x0")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 0u); + } + + SECTION("invalid values") { + auto input = GENERATE( + std::string("4294967296"), std::string("-1"), std::string("0o40000000000"), std::string("0x100000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_int64_t") { + int64_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64); + } + + SECTION("negative value") { + std::string input = "-64"; + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == -64); + } + + SECTION("max values") { + auto input = GENERATE( + std::string("9223372036854775807"), + std::string("0o777777777777777777777"), + std::string("0x7FFFFFFFFFFFFFFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 9223372036854775807); + } + + SECTION("minimum values") { + auto input = GENERATE( + std::string("-9223372036854775808"), + std::string("0o1000000000000000000000"), + std::string("0x8000000000000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == std::numeric_limits::min()); + } + + SECTION("invalid values") { + auto input = GENERATE( + std::string("9223372036854775808"), + std::string("-9223372036854775809"), + std::string("0o2000000000000000000000"), + std::string("0x10000000000000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atoi_uint64_t") { + uint64_t integer = 0; + + SECTION("positive values") { + auto input = GENERATE(std::string("64"), std::string("0o100"), std::string("0x40")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 64u); + } + + SECTION("max values") { + auto input = GENERATE( + std::string("18446744073709551615"), + std::string("0o1777777777777777777777"), + std::string("0xFFFFFFFFFFFFFFFF")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 18446744073709551615u); + } + + SECTION("minimum values") { + auto input = GENERATE(std::string("0"), std::string("0o0"), std::string("0x0")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == true); + REQUIRE(integer == 0u); + } + + SECTION("invalid values") { + auto input = GENERATE( + std::string("18446744073709551616"), + std::string("-1"), + std::string("0o2000000000000000000000"), + std::string("0x10000000000000000")); + REQUIRE(fkyaml::detail::atoi(input.begin(), input.end(), integer) == false); + } +} + +TEST_CASE("ScalarConv_atof_float") { + float fp = 0.f; + using limits_type = std::numeric_limits; + + SECTION("positive infinity") { + auto input = GENERATE( + std::string(".inf"), + std::string(".Inf"), + std::string(".INF"), + std::string("+.inf"), + std::string("+.Inf"), + std::string("+.INF")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isinf(fp)); + } + + SECTION("negative infinity") { + auto input = GENERATE(std::string("-.inf"), std::string("-.Inf"), std::string("-.INF")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isinf(fp)); + } + + SECTION("NaN") { + auto input = GENERATE(std::string(".nan"), std::string(".NaN"), std::string(".NAN")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isnan(fp)); + } + + SECTION("values") { + std::string input("3.14"); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp - 3.14f) < limits_type::epsilon()); + + input = "-3.14"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp + 3.14f) < limits_type::epsilon()); + + input = "-0.5"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp + 0.5f) < limits_type::epsilon()); + + input = ""; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == false); + + input = "3.40282347e+39"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == false); + } +} + +TEST_CASE("ScalarConv_atof_double") { + double fp = 0.; + using limits_type = std::numeric_limits; + + SECTION("positive infinity") { + auto input = GENERATE( + std::string(".inf"), + std::string(".Inf"), + std::string(".INF"), + std::string("+.inf"), + std::string("+.Inf"), + std::string("+.INF")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isinf(fp)); + } + + SECTION("negative infinity") { + auto input = GENERATE(std::string("-.inf"), std::string("-.Inf"), std::string("-.INF")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isinf(fp)); + } + + SECTION("NaN") { + auto input = GENERATE(std::string(".nan"), std::string(".NaN"), std::string(".NAN")); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::isnan(fp)); + } + + SECTION("values") { + std::string input("3.14"); + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp - 3.14) < limits_type::epsilon()); + + input = "-3.14"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp + 3.14) < limits_type::epsilon()); + + input = "-0.5"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == true); + REQUIRE(std::abs(fp + 0.5) < limits_type::epsilon()); + + input = ""; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == false); + + input = "1.7976931348623157E+309"; + REQUIRE(fkyaml::detail::atof(input.begin(), input.end(), fp) == false); + } +} diff --git a/test/unit_test/test_scalar_scanner_class.cpp b/test/unit_test/test_scalar_scanner_class.cpp index aeee394a..770e4b67 100644 --- a/test/unit_test/test_scalar_scanner_class.cpp +++ b/test/unit_test/test_scalar_scanner_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -11,75 +11,108 @@ #include TEST_CASE("ScalarScanner_Empty") { - REQUIRE(fkyaml::detail::scalar_scanner::scan("") == fkyaml::detail::lexical_token_t::STRING_VALUE); + fkyaml::detail::str_view token = ""; + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::STRING); } TEST_CASE("ScalarScanner_NullValue") { - auto token = GENERATE(std::string("~"), std::string("null"), std::string("Null"), std::string("NULL")); - REQUIRE(fkyaml::detail::scalar_scanner::scan(token) == fkyaml::detail::lexical_token_t::NULL_VALUE); + auto token = GENERATE( + fkyaml::detail::str_view("~"), + fkyaml::detail::str_view("null"), + fkyaml::detail::str_view("Null"), + fkyaml::detail::str_view("NULL")); + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::NULL_OBJECT); } TEST_CASE("ScalarScanner_BooleanValue") { auto token = GENERATE( - std::string("true"), - std::string("True"), - std::string("TRUE"), - std::string("false"), - std::string("False"), - std::string("FALSE")); - REQUIRE(fkyaml::detail::scalar_scanner::scan(token) == fkyaml::detail::lexical_token_t::BOOLEAN_VALUE); + fkyaml::detail::str_view("true"), + fkyaml::detail::str_view("True"), + fkyaml::detail::str_view("TRUE"), + fkyaml::detail::str_view("false"), + fkyaml::detail::str_view("False"), + fkyaml::detail::str_view("FALSE")); + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::BOOLEAN); } TEST_CASE("ScalarScanner_IntegerNumberValue") { auto token = GENERATE( - std::string("-1234"), - std::string("-853259"), - std::string("-1"), - std::string("0"), - std::string("643"), - std::string("+120"), - std::string("0o27"), - std::string("0o5"), - std::string("0o77772"), - std::string("0o672"), - std::string("0xA04F"), - std::string("0xa7F3"), - std::string("0xFf29Bc")); - REQUIRE(fkyaml::detail::scalar_scanner::scan(token) == fkyaml::detail::lexical_token_t::INTEGER_VALUE); + fkyaml::detail::str_view("-1234"), + fkyaml::detail::str_view("-853259"), + fkyaml::detail::str_view("-1"), + fkyaml::detail::str_view("0"), + fkyaml::detail::str_view("643"), + fkyaml::detail::str_view("+120"), + fkyaml::detail::str_view("0o27"), + fkyaml::detail::str_view("0o5"), + fkyaml::detail::str_view("0o77772"), + fkyaml::detail::str_view("0o672"), + fkyaml::detail::str_view("0xA04F"), + fkyaml::detail::str_view("0xa7F3"), + fkyaml::detail::str_view("0xFf29Bc")); + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::INTEGER); } TEST_CASE("ScalarScanner_FloatNumberValue") { auto token = GENERATE( - std::string(".inf"), - std::string(".Inf"), - std::string(".INF"), - std::string(".nan"), - std::string(".NaN"), - std::string(".NAN"), - std::string("-.inf"), - std::string("-.Inf"), - std::string("-.INF"), - std::string("-1.234"), - std::string("567.8"), - std::string("123."), - std::string("0.24"), - std::string("0."), - std::string("9.8e-3"), - std::string("3.95E3"), - std::string("1.863e+3")); - REQUIRE(fkyaml::detail::scalar_scanner::scan(token) == fkyaml::detail::lexical_token_t::FLOAT_NUMBER_VALUE); + fkyaml::detail::str_view(".inf"), + fkyaml::detail::str_view(".Inf"), + fkyaml::detail::str_view(".INF"), + fkyaml::detail::str_view(".nan"), + fkyaml::detail::str_view(".NaN"), + fkyaml::detail::str_view(".NAN"), + fkyaml::detail::str_view("-.inf"), + fkyaml::detail::str_view("-.Inf"), + fkyaml::detail::str_view("-.INF"), + fkyaml::detail::str_view("+.inf"), + fkyaml::detail::str_view("+.Inf"), + fkyaml::detail::str_view("+.INF"), + fkyaml::detail::str_view("-1.234"), + fkyaml::detail::str_view("-21."), + fkyaml::detail::str_view("567.8"), + fkyaml::detail::str_view("123."), + fkyaml::detail::str_view("0.24"), + fkyaml::detail::str_view("0."), + fkyaml::detail::str_view("9.8e-3"), + fkyaml::detail::str_view("3.95E3"), + fkyaml::detail::str_view("1.863e+3")); + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::FLOAT); } TEST_CASE("ScalarScanner_StringValue") { auto token = GENERATE( - std::string("abc"), - std::string("0th"), - std::string("0123"), - std::string("1.2.3"), - std::string("1.non-digit"), - std::string("-.foo"), - std::string("1exe"), - std::string("0oabc"), - std::string("0xyz")); - REQUIRE(fkyaml::detail::scalar_scanner::scan(token) == fkyaml::detail::lexical_token_t::STRING_VALUE); + fkyaml::detail::str_view("nullValue"), + fkyaml::detail::str_view("NullValue"), + fkyaml::detail::str_view("NULL_VALUE"), + fkyaml::detail::str_view("~Value"), + fkyaml::detail::str_view("trueValue"), + fkyaml::detail::str_view("TrueValue"), + fkyaml::detail::str_view("TRUE_VALUE"), + fkyaml::detail::str_view("falseValue"), + fkyaml::detail::str_view("falsy"), + fkyaml::detail::str_view("FalseValue"), + fkyaml::detail::str_view("Falsy"), + fkyaml::detail::str_view("FALSE_VALUE"), + fkyaml::detail::str_view(".infValue"), + fkyaml::detail::str_view(".InfValue"), + fkyaml::detail::str_view(".INFValue"), + fkyaml::detail::str_view(".nanValue"), + fkyaml::detail::str_view(".NaNValue"), + fkyaml::detail::str_view(".NANValue"), + fkyaml::detail::str_view("-.infValue"), + fkyaml::detail::str_view("-.InfValue"), + fkyaml::detail::str_view("-.INFValue"), + fkyaml::detail::str_view(".foo"), + fkyaml::detail::str_view("abc"), + fkyaml::detail::str_view("0th"), + fkyaml::detail::str_view("0123"), + fkyaml::detail::str_view("1.2.3"), + fkyaml::detail::str_view("1.23e"), + fkyaml::detail::str_view("1.2e-z"), + fkyaml::detail::str_view("1.non-digit"), + fkyaml::detail::str_view("-.foo"), + fkyaml::detail::str_view("1exe"), + fkyaml::detail::str_view("0oabc"), + fkyaml::detail::str_view("0xyz")); + REQUIRE(fkyaml::detail::scalar_scanner::scan(token.begin(), token.end()) == fkyaml::node_type::STRING); } diff --git a/test/unit_test/test_serializer_class.cpp b/test/unit_test/test_serializer_class.cpp index cc13fe1d..925c9396 100644 --- a/test/unit_test/test_serializer_class.cpp +++ b/test/unit_test/test_serializer_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_str_view_class.cpp b/test/unit_test/test_str_view_class.cpp new file mode 100644 index 00000000..4e032a13 --- /dev/null +++ b/test/unit_test/test_str_view_class.cpp @@ -0,0 +1,404 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include + +#include + +TEST_CASE("StrView_DefaultCtor") { + fkyaml::detail::str_view sv; + REQUIRE(sv.data() == nullptr); + REQUIRE(sv.size() == 0); + REQUIRE(sv.length() == 0); + REQUIRE(sv.empty()); +} + +TEST_CASE("StrView_CharPtrCtor") { + const char str[] = "hello world!"; + const char* p_str = &str[0]; + fkyaml::detail::str_view sv(p_str); + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == &str[0]); + REQUIRE(sv.end() == &str[0] + 12); +} + +TEST_CASE("StrView_StdStringCtor") { + std::string str = "hello world!"; + fkyaml::detail::str_view sv(str); + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == str.data()); + REQUIRE(sv.end() == str.data() + 12); +} + +TEST_CASE("StrView_CopyCtor") { + const char str[] = "hello world!"; + fkyaml::detail::str_view view = str; + fkyaml::detail::str_view sv(view); + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == &str[0]); + REQUIRE(sv.end() == &str[0] + 12); +} + +TEST_CASE("StrView_MoveCtor") { + const char str[] = "hello world!"; + fkyaml::detail::str_view view = str; + fkyaml::detail::str_view sv(std::move(view)); + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == &str[0]); + REQUIRE(sv.end() == &str[0] + 12); +} + +TEST_CASE("StrView_CopyAssignmentOperator") { + const char str[] = "hello world!"; + fkyaml::detail::str_view view = str; + fkyaml::detail::str_view sv {}; + sv = view; + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == &str[0]); + REQUIRE(sv.end() == &str[0] + 12); +} + +TEST_CASE("StrView_MoveAssignmentOperator") { + const char str[] = "hello world!"; + fkyaml::detail::str_view view = str; + fkyaml::detail::str_view sv {}; + sv = std::move(view); + + REQUIRE(sv.compare("hello world!") == 0); + REQUIRE(sv.size() == 12); + REQUIRE(sv.length() == 12); + REQUIRE(sv.begin() == &str[0]); + REQUIRE(sv.end() == &str[0] + 12); +} + +TEST_CASE("StrView_ReverseIterators") { + const char str[] = "hello world!"; + fkyaml::detail::str_view sv = str; + + REQUIRE(&*sv.rbegin() == &str[11]); + REQUIRE(sv.rend().operator->() + 1 == &str[0]); + REQUIRE(&*sv.crbegin() == &str[11]); + REQUIRE(sv.crend().operator->() + 1 == &str[0]); +} + +TEST_CASE("StrView_MaxSize") { + fkyaml::detail::str_view sv = "foo"; + REQUIRE(sv.max_size() == static_cast(std::numeric_limits::max())); +} + +TEST_CASE("StrView_SubscriptOperator") { + fkyaml::detail::str_view sv = "foo"; + REQUIRE(sv.size() == 3); + REQUIRE(sv[0] == 'f'); + REQUIRE(sv[1] == 'o'); + REQUIRE(sv[2] == 'o'); +} + +TEST_CASE("StrView_At") { + fkyaml::detail::str_view sv = "foo"; + REQUIRE(sv.size() == 3); + REQUIRE(sv.at(0) == 'f'); + REQUIRE(sv.at(1) == 'o'); + REQUIRE(sv.at(2) == 'o'); + REQUIRE_THROWS_AS(sv.at(3), fkyaml::out_of_range); + REQUIRE_THROWS_AS(sv.at(4), fkyaml::out_of_range); +} + +TEST_CASE("StrView_Front") { + const char str[] = "foo"; + fkyaml::detail::str_view sv = str; + REQUIRE(sv.front() == 'f'); + REQUIRE(&sv.front() == &str[0]); +} + +TEST_CASE("StrView_Back") { + const char str[] = "bar"; + fkyaml::detail::str_view sv = str; + REQUIRE(sv.back() == 'r'); + REQUIRE(&sv.back() == &str[2]); +} + +TEST_CASE("StrView_Data") { + const char str[] = "bar"; + fkyaml::detail::str_view sv = str; + REQUIRE(sv.data() == str); + + fkyaml::detail::str_view sv2 {}; + REQUIRE(sv2.data() == nullptr); +} + +TEST_CASE("StrView_RemovePrefix") { + const char str[] = "bar"; + fkyaml::detail::str_view sv = str; + REQUIRE(sv.data() == str); + REQUIRE(sv.size() == 3); + sv.remove_prefix(1); + REQUIRE(sv.data() == str + 1); + REQUIRE(sv.size() == 2); +} + +TEST_CASE("StrView_RemoveSuffix") { + const char str[] = "bar"; + fkyaml::detail::str_view sv = str; + REQUIRE(sv.data() == str); + REQUIRE(sv.size() == 3); + sv.remove_suffix(1); + REQUIRE(sv.data() == str); + REQUIRE(sv.size() == 2); +} + +TEST_CASE("StrView_Swap") { + fkyaml::detail::str_view sv = "foo"; + fkyaml::detail::str_view sv2 = "barr"; + const char* p_sv_data = sv.data(); + const char* p_sv2_data = sv2.data(); + + sv.swap(sv2); + REQUIRE(sv.size() == 4); + REQUIRE(sv.data() == p_sv2_data); + REQUIRE(sv2.size() == 3); + REQUIRE(sv2.data() == p_sv_data); +} + +TEST_CASE("StrView_Copy") { + fkyaml::detail::str_view sv = "xxxxxxxxxx"; + char buffer[] = "aaaaaaaaaa"; + + sv.copy(buffer, 4); + REQUIRE(std::strncmp(buffer, "xxxxaaaaaa", sizeof(buffer)) == 0); + + char buffer1[] = "bbbbbbbbbbbbbbbbbbbb"; + sv.copy(buffer1, 20 /*intentionally too large*/); + REQUIRE(std::strncmp(buffer1, "xxxxxxxxxxbbbbbbbbbb", sizeof(buffer1)) == 0); + + REQUIRE_THROWS_AS(sv.copy(buffer, 4, sv.length() + 1), fkyaml::out_of_range); +} + +TEST_CASE("StrView_Substr") { + fkyaml::detail::str_view sv = "xxxxxxxxxx"; + + REQUIRE(sv.substr(4) == "xxxxxx"); + REQUIRE(sv.substr(3, 2) == "xx"); + REQUIRE_THROWS_AS(sv.substr(20), fkyaml::out_of_range); +} + +TEST_CASE("StrView_Compare") { + fkyaml::detail::str_view sv = "a"; + REQUIRE(sv.compare("a") == 0); + REQUIRE(sv.compare("b") == -1); + REQUIRE(sv.compare("`") == 1); + + // skip checks of comparisons with a large string object if int == ptrdiff_t + if (static_cast(std::numeric_limits::max()) < std::numeric_limits::max()) { + constexpr std::size_t long_str_size = static_cast(std::numeric_limits::max()) + 4u; + char* p_long_str = (char*)std::malloc(long_str_size); + for (std::size_t i = 0; i < long_str_size - 1; i++) { + p_long_str[i] = 'a'; + } + p_long_str[long_str_size - 1] = '\0'; + fkyaml::detail::str_view view(p_long_str, long_str_size - 1); + + REQUIRE(sv.compare(p_long_str) == std::numeric_limits::min()); + fkyaml::detail::str_view view_4a = "aaaa"; + REQUIRE(view.compare(long_str_size - 1u - 4u, 4u, view_4a.data()) == 0); + REQUIRE(view.compare(long_str_size - 1u - 4u, 4u, view_4a.data(), view_4a.size()) == 0); + REQUIRE(view.compare(long_str_size - 1u - 4u, 4u, view_4a, 0, view_4a.size()) == 0); + REQUIRE(view.compare(long_str_size - 1u - 4u, 4u, view_4a) == 0); + REQUIRE(sv.compare(view) == std::numeric_limits::min()); + REQUIRE(view.compare(sv) == std::numeric_limits::max()); + + std::free(p_long_str); + p_long_str = nullptr; + } +} + +TEST_CASE("StrView_StartsWith") { + fkyaml::detail::str_view view_4a = "aaaa"; + fkyaml::detail::str_view sv = "aaaa"; + + REQUIRE(sv.starts_with(view_4a)); + REQUIRE(sv.starts_with('a')); + REQUIRE(sv.starts_with(view_4a.data())); +} + +TEST_CASE("StrView_EndsWith") { + fkyaml::detail::str_view view_4a = "aaaa"; + fkyaml::detail::str_view sv = "aaaa"; + + REQUIRE(sv.ends_with(view_4a)); + REQUIRE(sv.ends_with('a')); + REQUIRE(sv.ends_with(view_4a.data())); +} + +TEST_CASE("StrView_Contains") { + fkyaml::detail::str_view sv = "aaaa"; + fkyaml::detail::str_view view = "aa"; + + REQUIRE(sv.contains(view)); + REQUIRE(sv.contains("a")); + REQUIRE(sv.contains('a')); +} + +TEST_CASE("StrView_Find") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.find('a', 0) == 0); + REQUIRE(sv.find('a', sv.size()) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find('e', 0) == fkyaml::detail::str_view::npos); + + REQUIRE(sv.find("aa", 0, 2) == 0); + REQUIRE(sv.find("aa", 2, 2) == 2); + REQUIRE(sv.find("aa", 2, 0) == 2); + REQUIRE(sv.find("aa", sv.size() + 1, 0) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find("aa", sv.size() + 1, 2) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find("bcd", 0, 3) == 5); + REQUIRE(sv.find("efg", 0, 3) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find("aaaabbcde", 0, 9) == fkyaml::detail::str_view::npos); +} + +TEST_CASE("StrView_ReverseFind") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.rfind('a') == 3); + REQUIRE(sv.rfind('a', 0) == 0); + REQUIRE(sv.rfind('e') == fkyaml::detail::str_view::npos); + + fkyaml::detail::str_view empty_sv = ""; + REQUIRE(empty_sv.rfind('a', 0)); + + REQUIRE(sv.rfind("aaaa", 0) == 0); + REQUIRE(sv.rfind("aaaa", 0, 4) == 0); + REQUIRE(sv.rfind("aaaa", 0, sv.size() + 1) == fkyaml::detail::str_view::npos); + REQUIRE(sv.rfind("efg", 2, 3) == fkyaml::detail::str_view::npos); +} + +TEST_CASE("StrView_FindFirstOf") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.find_first_of('b', 2) == 4); + REQUIRE(sv.find_first_of("abc") == 0); + REQUIRE(sv.find_first_of("abc", 0, 3) == 0); + REQUIRE(sv.find_first_of("abc", 0, 0) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find_first_of("efg", 0, 3) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find_first_of(fkyaml::detail::str_view {"abc"}) == 0); +} + +TEST_CASE("StrView_FindLastOf") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.find_last_of('a') == 3); + REQUIRE(sv.find_last_of("a") == 3); + REQUIRE(sv.find_last_of("a", 0, 1) == 0); + REQUIRE(sv.find_last_of("a", 0, sv.size() + 1) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find_last_of(fkyaml::detail::str_view {"aa"}) == 3); +} + +TEST_CASE("StrView_FindFirstNotOf") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.find_first_not_of('a') == 4); + REQUIRE(sv.find_first_not_of('a', sv.size()) == fkyaml::detail::str_view::npos); + + REQUIRE(sv.find_first_not_of("a") == 4); + REQUIRE(sv.find_first_not_of("a", 3) == 4); + REQUIRE(sv.find_first_not_of("a", sv.size()) == fkyaml::detail::str_view::npos); + REQUIRE(sv.find_first_not_of(fkyaml::detail::str_view {"a"}) == 4); +} + +TEST_CASE("StrView_FindLastNotOf") { + fkyaml::detail::str_view sv = "aaaabbcd"; + + REQUIRE(sv.find_last_not_of('d') == 6); + REQUIRE(sv.find_last_not_of('b', 5) == 3); + fkyaml::detail::str_view empty_sv = ""; + REQUIRE(empty_sv.find_last_not_of('b') == fkyaml::detail::str_view::npos); + + REQUIRE(sv.find_last_not_of("bcd") == 3); + REQUIRE(sv.find_last_not_of("bcd", 5) == 3); + REQUIRE(empty_sv.find_last_not_of("bcd") == fkyaml::detail::str_view::npos); + REQUIRE(sv.find_last_not_of(fkyaml::detail::str_view {"bcd"}) == 3); +} + +TEST_CASE("StrView_EqualToOperator") { + fkyaml::detail::str_view sv0 = "abc"; + fkyaml::detail::str_view sv1 = "abc"; + std::string str = "abc"; + const char arr[] = "abc"; + REQUIRE(sv0 == sv1); + REQUIRE(sv0 == str); + REQUIRE(str == sv0); + REQUIRE(sv0 == arr); + REQUIRE(arr == sv0); +} + +TEST_CASE("StrView_NotEqualToOperator") { + fkyaml::detail::str_view sv0 = "abc"; + fkyaml::detail::str_view sv1 = "edf"; + std::string str = "edf"; + const char arr[] = "edf"; + REQUIRE(sv0 != sv1); + REQUIRE(sv0 != str); + REQUIRE(str != sv0); + REQUIRE(sv0 != arr); + REQUIRE(arr != sv0); +} + +TEST_CASE("StrView_LessThanOperator") { + fkyaml::detail::str_view sv = "a"; + REQUIRE_FALSE(sv < fkyaml::detail::str_view {"a"}); + REQUIRE(sv < fkyaml::detail::str_view {"b"}); + REQUIRE_FALSE(sv < fkyaml::detail::str_view {"`"}); +} + +TEST_CASE("StrView_LessThanOrEqualToOperator") { + fkyaml::detail::str_view sv = "a"; + REQUIRE(sv <= fkyaml::detail::str_view {"a"}); + REQUIRE(sv <= fkyaml::detail::str_view {"b"}); + REQUIRE_FALSE(sv <= fkyaml::detail::str_view {"`"}); +} + +TEST_CASE("StrView_GreaterThanOperator") { + fkyaml::detail::str_view sv = "a"; + REQUIRE_FALSE(sv > fkyaml::detail::str_view {"a"}); + REQUIRE_FALSE(sv > fkyaml::detail::str_view {"b"}); + REQUIRE(sv > fkyaml::detail::str_view {"`"}); +} + +TEST_CASE("StrView_GreaterThanOrEqualToOperator") { + fkyaml::detail::str_view sv = "a"; + REQUIRE(sv >= fkyaml::detail::str_view {"a"}); + REQUIRE_FALSE(sv >= fkyaml::detail::str_view {"b"}); + REQUIRE(sv >= fkyaml::detail::str_view {"`"}); +} + +TEST_CASE("StrView_InsertionOperator") { + std::ostringstream ss {}; + fkyaml::detail::str_view sv = "abc"; + REQUIRE(ss.str().empty()); + ss << sv; + REQUIRE(ss.str() == "abc"); +} diff --git a/test/unit_test/test_string_formatter.cpp b/test/unit_test/test_string_formatter.cpp index 8b370672..e9b360a0 100644 --- a/test/unit_test/test_string_formatter.cpp +++ b/test/unit_test/test_string_formatter.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_tag_resolver_class.cpp b/test/unit_test/test_tag_resolver_class.cpp index 6efd43ec..495f058a 100644 --- a/test/unit_test/test_tag_resolver_class.cpp +++ b/test/unit_test/test_tag_resolver_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/test/unit_test/test_uri_encoding_class.cpp b/test/unit_test/test_uri_encoding_class.cpp index 2466c6ff..233a65a6 100644 --- a/test/unit_test/test_uri_encoding_class.cpp +++ b/test/unit_test/test_uri_encoding_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -21,7 +21,7 @@ TEST_CASE("URIEncoding_Validate") { std::string("0123456789"), std::string("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), std::string("abcdefghijklmnopqrstuvwxyz")); - REQUIRE(fkyaml::detail::uri_encoding::validate(input.begin(), input.end())); + REQUIRE(fkyaml::detail::uri_encoding::validate(input.c_str(), input.c_str() + input.size())); } SECTION("invalid URI characters") { @@ -76,6 +76,6 @@ TEST_CASE("URIEncoding_Validate") { std::string("`"), std::string("|"), std::string("\x7F")); - REQUIRE_FALSE(fkyaml::detail::uri_encoding::validate(input.begin(), input.end())); + REQUIRE_FALSE(fkyaml::detail::uri_encoding::validate(input.c_str(), input.c_str() + input.size())); } } diff --git a/test/unit_test/test_encode_detector.cpp b/test/unit_test/test_utf_encode_detector.cpp similarity index 69% rename from test/unit_test/test_encode_detector.cpp rename to test/unit_test/test_utf_encode_detector.cpp index a62473e3..5ac4371e 100644 --- a/test/unit_test/test_encode_detector.cpp +++ b/test/unit_test/test_utf_encode_detector.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -23,7 +23,7 @@ #define ENABLE_C4996 #endif -TEST_CASE("EncodeDetector_DetectEncodingType") { +TEST_CASE("UTFEncodeDetector_DetectEncodingType") { struct test_data_t { test_data_t(std::array input_, fkyaml::detail::utf_encode_t encode_type_, bool has_bom_) : input(input_), @@ -85,7 +85,7 @@ TEST_CASE("EncodeDetector_DetectEncodingType") { REQUIRE(has_bom == d.has_bom); } -TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { +TEST_CASE("UTFEncodeDetector_DetectEncodingAndSkipBom") { //////////////////////// // char iterators // //////////////////////// @@ -94,7 +94,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0x60u), char(0x61u), char(0x62u), char(0x63u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(begin == std::begin(input)); } @@ -103,7 +104,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0xEFu), char(0xBBu), char(0xBFu), char(0x60u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(begin == std::begin(input) + 3); } @@ -112,7 +114,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {0, char(0x60u), 0, char(0x61u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(begin == std::begin(input)); } @@ -121,7 +124,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0xFEu), char(0xFFu), 0, char(0x60u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(begin == std::begin(input) + 2); } @@ -130,7 +134,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0x60u), 0, char(0x61u), 0}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(begin == std::begin(input)); } @@ -139,7 +144,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0xFFu), char(0xFEu), char(0x60u), 0}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(begin == std::begin(input) + 2); } @@ -148,7 +154,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {0, 0, 0, char(0x60u), 0, 0, 0, char(0x61u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(begin == std::begin(input)); } @@ -157,7 +164,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {0, 0, char(0xFEu), char(0xFFu), 0, 0, 0, char(0x60u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(begin == std::begin(input) + 4); } @@ -166,7 +174,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0x60u), 0, 0, 0, char(0x61u), 0, 0, 0}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(begin == std::begin(input)); } @@ -175,11 +184,68 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::string input {char(0xFFu), char(0xFEu), 0, 0, char(0x60u), 0, 0, 0}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(begin == std::begin(input) + 4); } + SECTION("empty char iterators") { + std::string input = ""; + auto begin = std::begin(input); + auto end = std::end(input); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); + REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); + REQUIRE(begin == std::begin(input)); + } + + /////////////////////////// + // char8_t iterators // + /////////////////////////// + +#if FK_YAML_HAS_CHAR8_T + + SECTION("char8_t iterators encoded in the UTF-8") { + std::u8string input {char8_t(0x60u), char8_t(0x61u), char8_t(0x62u), char8_t(0x63u)}; + auto begin = std::begin(input); + auto end = std::end(input); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); + REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); + REQUIRE(begin == std::begin(input)); + } + + SECTION("char8_t iterators encoded in the UTF-8(BOM)") { + std::u8string input {char8_t(0xEFu), char8_t(0xBBu), char8_t(0xBFu), char8_t(0x60u)}; + auto begin = std::begin(input); + auto end = std::end(input); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); + REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); + REQUIRE(begin == std::begin(input) + 3); + } + + SECTION("empty char8_t iterators") { + std::u8string input = u8""; + auto begin = std::begin(input); + auto end = std::end(input); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); + REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); + REQUIRE(begin == std::begin(input)); + } + + SECTION("char8_t iterators with invalid encoding") { + std::u8string input {char8_t(0x00u), char8_t(0x00u), char8_t(0xFEu), char8_t(0xFFu)}; + auto begin = std::begin(input); + auto end = std::end(input); + using iterator_type = decltype(begin); + REQUIRE_THROWS_AS(fkyaml::detail::utf_encode_detector::detect(begin, end), fkyaml::exception); + } + +#endif // FK_YAML_HAS_CHAR8_T + //////////////////////////// // char16_t iterators // //////////////////////////// @@ -188,7 +254,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input {char16_t(0x0060u), char16_t(0x0061u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(begin == std::begin(input)); } @@ -197,7 +264,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input {char16_t(0xFEFFu), char16_t(0x0060u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(begin == std::begin(input) + 1); } @@ -206,7 +274,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input {char16_t(0x6000u), char16_t(0x6100u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(begin == std::begin(input)); } @@ -215,7 +284,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input {char16_t(0xFFFEu), char16_t(0x6000u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(begin == std::begin(input) + 1); } @@ -224,7 +294,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input = u""; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(begin == std::begin(input)); } @@ -233,7 +304,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u16string input {char16_t(0x0000u), char16_t(0xFEFFu)}; auto begin = std::begin(input); auto end = std::end(input); - REQUIRE_THROWS_AS(fkyaml::detail::detect_encoding_and_skip_bom(begin, end), fkyaml::exception); + using iterator_type = decltype(begin); + REQUIRE_THROWS_AS(fkyaml::detail::utf_encode_detector::detect(begin, end), fkyaml::exception); } //////////////////////////// @@ -244,7 +316,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input {char32_t(0x00000060u), char32_t(0x00000061u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(begin == std::begin(input)); } @@ -253,7 +326,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input {char32_t(0x0000FEFFu), char32_t(0x00000060u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(begin == std::begin(input) + 1); } @@ -262,7 +336,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input {char32_t(0x60000000u), char32_t(0x61000000u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(begin == std::begin(input)); } @@ -271,7 +346,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input {char32_t(0xFFFE0000u), char32_t(0x60000000u)}; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(begin == std::begin(input) + 1); } @@ -280,7 +356,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input = U""; auto begin = std::begin(input); auto end = std::end(input); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(begin, end); + using iterator_type = decltype(begin); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::utf_encode_detector::detect(begin, end); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(begin == std::begin(input)); } @@ -289,7 +366,8 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { std::u32string input {char32_t(0xFEFF0060u), char32_t(0x00610062u)}; auto begin = std::begin(input); auto end = std::end(input); - REQUIRE_THROWS_AS(fkyaml::detail::detect_encoding_and_skip_bom(begin, end), fkyaml::exception); + using iterator_type = decltype(begin); + REQUIRE_THROWS_AS(fkyaml::detail::utf_encode_detector::detect(begin, end), fkyaml::exception); } ////////////////////// @@ -302,7 +380,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(std::ftell(p_file) == 0); @@ -315,7 +393,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(std::ftell(p_file) == 3); @@ -328,7 +406,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(std::ftell(p_file) == 0); @@ -341,7 +419,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(std::ftell(p_file) == 2); @@ -354,7 +432,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(std::ftell(p_file) == 0); @@ -367,7 +445,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(std::ftell(p_file) == 2); @@ -380,7 +458,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(std::ftell(p_file) == 0); @@ -393,7 +471,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(std::ftell(p_file) == 4); @@ -406,7 +484,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(std::ftell(p_file) == 0); @@ -419,7 +497,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(std::ftell(p_file) == 4); @@ -432,7 +510,7 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { ENABLE_C4996 REQUIRE(p_file != nullptr); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(p_file); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::file_utf_encode_detector::detect(p_file); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(std::ftell(p_file) == 0); @@ -445,77 +523,77 @@ TEST_CASE("EncodeDetector_DetectEncodingAndSkipBom") { SECTION("std::istream with UTF-8 encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8n.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(ifs.tellg() == 0); } SECTION("std::istream with UTF-8(BOM) encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf8bom.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(ifs.tellg() == 3); } SECTION("std::istream with UTF-16BE encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf16ben.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(ifs.tellg() == 0); } SECTION("std::istream with UTF-16BE(BOM) encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf16bebom.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16BE); REQUIRE(ifs.tellg() == 2); } SECTION("std::istream with UTF-16LE encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf16len.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(ifs.tellg() == 0); } SECTION("std::istream with UTF-16LE(BOM) encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf16lebom.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_16LE); REQUIRE(ifs.tellg() == 2); } SECTION("std::istream with UTF-32BE encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf32ben.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(ifs.tellg() == 0); } SECTION("std::istream with UTF-32BE(BOM) encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf32bebom.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32BE); REQUIRE(ifs.tellg() == 4); } SECTION("std::istream with UTF-32LE encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf32len.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(ifs.tellg() == 0); } SECTION("std::istream with UTF-32LE(BOM) encoding") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/input_adapter_test_data_utf32lebom.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_32LE); REQUIRE(ifs.tellg() == 4); } SECTION("std::istream with an empty input file") { std::ifstream ifs(FK_YAML_TEST_DATA_DIR "/single_char_byte_input.txt"); - fkyaml::detail::utf_encode_t ret = fkyaml::detail::detect_encoding_and_skip_bom(ifs); + fkyaml::detail::utf_encode_t ret = fkyaml::detail::stream_utf_encode_detector::detect(ifs); REQUIRE(ret == fkyaml::detail::utf_encode_t::UTF_8); REQUIRE(ifs.tellg() == 0); } diff --git a/test/unit_test/test_utf_encodings.cpp b/test/unit_test/test_utf_encodings.cpp index aab46ec0..06dfa6a8 100644 --- a/test/unit_test/test_utf_encodings.cpp +++ b/test/unit_test/test_utf_encodings.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -143,14 +143,20 @@ TEST_CASE("UTF8_Validate") { REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0xBFu), uint8_t(0xBFu)}) == true); + REQUIRE( + fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0x8Fu), uint8_t(0x80u), uint8_t(0x80u)}) == false); REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xC0u), uint8_t(0xBFu), uint8_t(0xBFu)}) == false); REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xC1u), uint8_t(0xBFu), uint8_t(0xBFu)}) == false); + REQUIRE( + fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0x7Fu), uint8_t(0xBFu)}) == false); REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0xC0u), uint8_t(0xBFu)}) == false); REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0xC1u), uint8_t(0xBFu)}) == false); + REQUIRE( + fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0xBFu), uint8_t(0x7Fu)}) == false); REQUIRE( fkyaml::detail::utf8::validate({uint8_t(0xF0u), uint8_t(0xBFu), uint8_t(0xBFu), uint8_t(0xC0u)}) == false); REQUIRE( diff --git a/test/unit_test/test_yaml_escaper_class.cpp b/test/unit_test/test_yaml_escaper_class.cpp index 4682f195..b5ba72ed 100644 --- a/test/unit_test/test_yaml_escaper_class.cpp +++ b/test/unit_test/test_yaml_escaper_class.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani @@ -14,7 +14,7 @@ TEST_CASE("YamlEscaper_Unescape") { SECTION("valid escape sequence") { - using test_data_t = std::pair; + using test_data_t = std::pair; auto test_data = GENERATE( test_data_t {"\\a", "\a"}, test_data_t {"\\b", "\b"}, @@ -44,20 +44,20 @@ TEST_CASE("YamlEscaper_Unescape") { test_data_t {"\\U0000007F", {char(0x7F)}}); std::string buff {}; - auto begin_itr = test_data.first.cbegin(); - auto end_itr = test_data.first.cend(); + auto begin_itr = test_data.first.begin(); + auto end_itr = test_data.first.end(); REQUIRE(fkyaml::detail::yaml_escaper::unescape(begin_itr, end_itr, buff)); REQUIRE(buff == test_data.second); } SECTION("invalid escape sequence") { auto input = GENERATE( - std::string("\\Q"), - std::string("\\xw"), - std::string("\\x+"), - std::string("\\u="), - std::string("\\U^"), - std::string("\\x{")); + fkyaml::detail::str_view("\\Q"), + fkyaml::detail::str_view("\\xw"), + fkyaml::detail::str_view("\\x+"), + fkyaml::detail::str_view("\\u="), + fkyaml::detail::str_view("\\U^"), + fkyaml::detail::str_view("\\x{")); std::string buff {}; auto begin_itr = input.cbegin(); @@ -66,7 +66,7 @@ TEST_CASE("YamlEscaper_Unescape") { } SECTION("invalid UTF encoding") { - std::string input = "\\U00110000"; + fkyaml::detail::str_view input = "\\U00110000"; auto begin_itr = input.cbegin(); auto end_itr = input.cend(); std::string buff {}; @@ -116,8 +116,7 @@ TEST_CASE("YamlEscaper_Escape") { test_data_t {{char(0xE2u), char(0x80u), char(0xA9u)}, "\\P"}); bool is_escaped = false; - auto begin_itr = test_data.first.cbegin(); - auto end_itr = test_data.first.cend(); - REQUIRE(fkyaml::detail::yaml_escaper::escape(begin_itr, end_itr, is_escaped) == test_data.second); + fkyaml::detail::str_view input = test_data.first; + REQUIRE(fkyaml::detail::yaml_escaper::escape(input.begin(), input.end(), is_escaped) == test_data.second); REQUIRE(is_escaped); } diff --git a/test/unit_test/test_yaml_version_type.cpp b/test/unit_test/test_yaml_version_type.cpp new file mode 100644 index 00000000..062f096a --- /dev/null +++ b/test/unit_test/test_yaml_version_type.cpp @@ -0,0 +1,21 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#include + +#include + +#include + +TEST_CASE("YamlVersionType_ToString") { + using test_data_t = std::pair; + auto test_data = GENERATE( + test_data_t {fkyaml::yaml_version_type::VERSION_1_1, "VERSION_1_1"}, + test_data_t {fkyaml::yaml_version_type::VERSION_1_2, "VERSION_1_2"}); + REQUIRE(test_data.second == fkyaml::to_string(test_data.first)); +} diff --git a/tool/benchmark/main.cpp b/tool/benchmark/main.cpp index 4b40fe8b..14d6aff7 100644 --- a/tool/benchmark/main.cpp +++ b/tool/benchmark/main.cpp @@ -1,6 +1,6 @@ // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library (supporting code) -// | __| _ < \_ _/| ___ | _ | |___ version 0.3.11 +// | __| _ < \_ _/| ___ | _ | |___ version 0.3.12 // |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML // // SPDX-FileCopyrightText: 2023-2024 Kensuke Fukutani diff --git a/tool/benchmark/result_debug.log b/tool/benchmark/result_debug.log index 781144a8..7ba5bd5b 100644 --- a/tool/benchmark/result_debug.log +++ b/tool/benchmark/result_debug.log @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... ------------------------------------------------------------------------------------- -bm_fkyaml_parse 121413 ns 121413 ns 5836 bytes_per_second=35.2759Mi/s items_per_second=8.23636k/s +bm_fkyaml_parse 121413 ns 121413 ns 5836 bytes_per_second=39.1517Mi/s items_per_second=9.14129k/s bm_yamlcpp_parse 4963184 ns 4963204 ns 141 bytes_per_second=883.651Ki/s items_per_second=201.483/s bm_libfyaml_parse 595741 ns 595744 ns 1185 bytes_per_second=7.18924Mi/s items_per_second=1.67857k/s bm_rapidyaml_parse_inplace 238052 ns 238052 ns 3007 bytes_per_second=17.9916Mi/s items_per_second=4.20075k/s diff --git a/tool/benchmark/result_release.log b/tool/benchmark/result_release.log index e1c9803c..b80e5952 100644 --- a/tool/benchmark/result_release.log +++ b/tool/benchmark/result_release.log @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... ------------------------------------------------------------------------------------- -bm_fkyaml_parse 105774 ns 105775 ns 6662 bytes_per_second=40.4911Mi/s items_per_second=9.45402k/s +bm_fkyaml_parse 105774 ns 105775 ns 6662 bytes_per_second=41.0509Mi/s items_per_second=9.58472k/s bm_yamlcpp_parse 579017 ns 579019 ns 1202 bytes_per_second=7.39692Mi/s items_per_second=1.72706k/s bm_libfyaml_parse 137681 ns 137669 ns 5726 bytes_per_second=31.1104Mi/s items_per_second=7.26379k/s bm_rapidyaml_parse_inplace 29092 ns 29092 ns 24046 bytes_per_second=147.221Mi/s items_per_second=34.3737k/s diff --git a/tool/natvis_generator/params.json b/tool/natvis_generator/params.json index be48238b..45c880b7 100644 --- a/tool/natvis_generator/params.json +++ b/tool/natvis_generator/params.json @@ -1 +1 @@ -{ "version": "0.3.11" } +{ "version": "0.3.12" }